[
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: \"maven\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n"
  },
  {
    "path": ".github/workflows/maven.yml",
    "content": "# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time\n# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven\n\n# This workflow uses actions that are not certified by GitHub.\n# They are provided by a third-party and are governed by\n# separate terms of service, privacy policy, and support\n# documentation.\n\nname: Java CI with Maven\n\non:\n  push:\n    branches: [\"master\"]\n    paths:\n      - \"src/**\"\n      - \"pom.xml\"\n  pull_request:\n    branches: [\"master\"]\n    paths:\n      - \"src/**\"\n      - \"pom.xml\"\n\njobs:\n  build:\n\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v3\n    - name: Set up Z3\n      uses: pavpanchekha/setup-z3@v1.3\n      with:\n        version: '4.12.2'\n    - name: Set up JDK 21\n      uses: actions/setup-java@v3\n      with:\n        java-version: '21'\n        distribution: 'temurin'\n        cache: maven\n    - name: Verify with Maven (includes building)\n      run: mvn -B verify --file pom.xml\n\n"
  },
  {
    "path": ".github/workflows/pages.yml",
    "content": "# This workflow uses actions that are not certified by GitHub.\n# They are provided by a third-party and are governed by\n# separate terms of service, privacy policy, and support\n# documentation.\n\n# Sample workflow for building and deploying a Jekyll site to GitHub Pages\nname: Deploy Jekyll site to Pages\n\non:\n  push:\n    branches: [\"master\"]\n    paths: [\"docs/**\"]\n\n  # Allows you to run this workflow manually from the Actions tab\n  workflow_dispatch:\n\n# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages\npermissions:\n  contents: read\n  pages: write\n  id-token: write\n\n# Allow one concurrent deployment\nconcurrency:\n  group: \"pages\"\n  cancel-in-progress: true\n\njobs:\n  # Build job\n  build:\n    runs-on: ubuntu-latest\n    defaults:\n      run:\n        working-directory: docs\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n      - name: Setup Ruby\n        uses: ruby/setup-ruby@v1\n        with:\n          ruby-version: '3.3' # Not needed with a .ruby-version file\n          bundler-cache: true # runs 'bundle install' and caches installed gems automatically\n          cache-version: 0 # Increment this number if you need to re-download cached gems\n          working-directory: '${{ github.workspace }}/docs'\n      - name: Setup Pages\n        id: pages\n        uses: actions/configure-pages@v5\n      - name: Build with Jekyll\n        # Outputs to the './_site' directory by default\n        run: bundle exec jekyll build --baseurl \"${{ steps.pages.outputs.base_path }}\"\n        env:\n          JEKYLL_ENV: production\n      - name: Upload artifact\n        # Automatically uploads an artifact from the './_site' directory by default\n        uses: actions/upload-pages-artifact@v3\n        with:\n          path: \"docs/_site/\"\n\n  # Deployment job\n  deploy:\n    environment:\n      name: github-pages\n      url: ${{ steps.deployment.outputs.page_url }}\n    runs-on: ubuntu-latest\n    needs: build\n    steps:\n      - name: Deploy to GitHub Pages\n        id: deployment\n        uses: actions/deploy-pages@v4\n"
  },
  {
    "path": ".github/workflows/submit-deps.yml",
    "content": "# Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive\n\nname: Submit Maven Dependencies\n\non:\n  push:\n    branches: [\"master\"]\n\npermissions:\n  contents: write\n\njobs:\n  submit-maven-deps:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@v3\n      - uses: actions/setup-java@v3\n        with:\n          java-version: '21'\n          distribution: 'temurin'\n      - name: Submit Maven dependency graph\n        uses: advanced-security/maven-dependency-submission-action@v5\n"
  },
  {
    "path": ".gitignore",
    "content": "# Compiled class file\n*.class\n\n# Log file\n*.log\n\n# BlueJ files\n*.ctxt\n\n# Mobile Tools for Java (J2ME)\n.mtj.tmp/\n\n# Package Files #\n*.jar\n*.war\n*.ear\n*.zip\n*.tar.gz\n*.rar\n\n# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml\nhs_err_pid*\ntarget/\n\n# Eclipse stuff\n.classpath\n.project\n.settings/\n.metadata/\n\n# VSCode stuff\n.vscode/\n\n# Vim swap files\n*.swp\n\n# VS code workspace\n*.code-workspace\n\n# backup files\n*~\n.DS_Store\n\n# Formulog stuff\n/codegen/\n/src/main/resources/codegen/build/\n\n.idea/\n\n# Jekyll\n_site/\n"
  },
  {
    "path": "Dockerfile",
    "content": "# To upload the Docker images to Dockerhub, log into the Docker console, and then run\n#\n#   docker buildx build --push --platform linux/amd64,linux/arm64 -t aaronbembenek/formulog:X.Y.Z .\n#\n# (with the appropriate version number substituted for X.Y.Z).\n\nFROM maven:3.8.6-openjdk-11 AS build\nWORKDIR /root/formulog/\nCOPY src src\nCOPY pom.xml pom.xml \nRUN mvn package -DskipTests\n\nFROM ubuntu:23.04\nARG version=0.8.0-SNAPSHOT\nWORKDIR /root/\nRUN apt-get update \\\n  && DEBIAN_FRONTEND=noninteractive \\\n  apt-get install -y \\\n  openjdk-11-jre \\\n  z3 \\\n  libboost1.81-all-dev \\\n  libtbb-dev \\\n  bison \\\n  build-essential \\\n  clang \\\n  cmake \\\n  doxygen \\\n  flex \\\n  g++ \\\n  git \\\n  libffi-dev \\\n  libncurses5-dev \\\n  libsqlite3-dev \\\n  make \\\n  mcpp \\\n  python3 \\\n  sqlite3 \\\n  zlib1g-dev \\\n  # Install modified Souffle\n  && git clone --branch eager-eval https://github.com/aaronbembenek/souffle.git \\\n  && cd souffle \\\n  && cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DSOUFFLE_ENABLE_TESTING=OFF \\\n  && cmake --build build -j$(nproc) \\\n  && cmake --build build --target install \\\n  && cmake --build build --target clean\nWORKDIR /root/formulog/\nCOPY --from=build /root/formulog/target/formulog-${version}-jar-with-dependencies.jar formulog.jar\nCOPY examples examples\n"
  },
  {
    "path": "LICENSE.txt",
    "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": "README.md",
    "content": "# Formulog\n\n![Build Status](https://github.com/HarvardPL/formulog/actions/workflows/maven.yml/badge.svg)\n\n**TL;DR: write SMT-based program analyses (symbolic executors, refinement type checkers, etc.) in an optimized Datalog-like language.**\n\nDatalog has proven to be a useful language for implementing a range of program analyses, but analyses that use SMT solving cannot be easily written in traditional versions of Datalog.\nFormulog sets out to fill this gap by augmenting Datalog with ways to construct and reason about SMT formulas, as well as some first-order functional programming to make life easier.\n\n**Why write your SMT-based analysis in Formulog?**\n\n1. By combining logic programming, functional programming, and SMT solving, Formulog makes it possible to encode many analyses declaratively at the level of mathematical specification (e.g., inference rules), closing the gap between specification and implementation---and often revealing bugs in the spec!\n2. This high-level encoding makes it possible for Formulog to apply high-level optimizations to your analysis, like automatic parallelization and goal-directed evaluation.\n3. Thanks to our [Formulog-to-Soufflé compiler](https://harvardpl.github.io/formulog/eval_modes/compile.html), you can automatically generate a C++ version of the analysis that leverages highly optimized Datalog algorithms and data structures.\n\n**Interested?**\nFor more information, check out the [Formulog docs](https://harvardpl.github.io/formulog/) (also available in the [docs](./docs/) directory), including [tips on getting started](https://harvardpl.github.io/formulog/starting.html) and the [language reference](https://harvardpl.github.io/formulog/lang_ref/).\nTo get a sense for what's involved in building a nontrivial SMT-based analysis in Formulog,\ncheck out our [tutorial](https://harvardpl.github.io/formulog/tutorial/) on implementing a refinement type checker in Formulog.\n\n## Contributing\n\nContributions to this project are most welcome!\nPlease open a [GitHub issue](https://github.com/HarvardPL/formulog/issues/new) and then link a pull request to it.\nPull requests must be in the [Google Java format](https://github.com/google/google-java-format) before being merged.\nTo reformat your code, run `mvn spotless:apply`; you can also check if your code is conformant (without reformatting it) by running `mvn spotless:check`.\n\n## Licensing and Third-Party Libraries\n\nFormulog is released under an [Apache 2.0 license](./LICENSE.txt).\n\nThis project uses third-party libraries. You can generate a list of these\nlibraries and download their associated licenses with this command:\n\n```\nmvn license:download-licenses\n```\n\nThe generated content can be found in the `target/generated-resources/`\ndirectory.\n"
  },
  {
    "path": "changelog.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\n## [0.8.0] - 2024-10-18\n\n### Added\n\n- Support for eager evaluation in both interpreter (`--eager-eval` option) and compiler.\n- Reorganized documentation and added a lot of new material, including a tutorial.\n- Apply Google Java format with Maven.\n- Various improvements to the code generator.\n\n### Changed\n\n- Better error reporting for type arity mismatch.\n- Clean up CI.\n- Better, more consistent CLI options for interpreter and generated code.\n\n### Fixed\n\n- Lexing of arithmetic expressions without spaces.\n- Various (rare) interpreter bugs.\n- Various bugs in the C++ runtime and generated code.\n\n## [0.7.0] - 2023-02-14\n\n### Added\n\n- Support for compilation to Souffle/C++ (`--codegen` option).\n\n### Changed\n\n- Removed built-in functions `substitute` and `is_free`.\n\n## [0.6.0] - 2022-08-31\n\n### Added\n\n- Support for `bv_to_int`, `int_to_bv`, `bv_extract`, and `bv_concat` formula\n  constructors.\n- More string manipulation and inspection functions (`substring`,\n  `string_length`, `char_at`, `string_to_list`, `list_to_string`,\n  `string_to_i32`, and `string_to_i64`).\n- Allow argument annotations in relation declarations.\n- Option to output relations to disk (`@disk` annotation).\n- Explicit annotation `@edb` for EDB relations.\n\n### Changed\n\n- New and improved command line interface.\n- New syntax for relation declarations (`rel` instead of `input` and `output`).\n- Removed `@external` annotation (replaced by `@disk`).\n\n### Fixed\n\n- Incorrect (non-`smt`) types for formula constructors.\n- Various lacunae in documentation.\n- Various bugs (including some type checking bugs).\n\n## [0.5.0] - 2020-11-15\n\n### Added\n\n- Give line numbers with parse exceptions.\n\n### Changed\n\n- Signature of `get_model` (takes a list of propositions now)\n\n### Fixed\n\n- Bug in the naive (call-reset) SMT solver strategy.\n- Bug in `let fun` expressions.\n\n## [0.4.0] - 2020-09-22\n\n### Added\n\n- Eager semi-naive evaluation algorithm.\n- SMT manager that uses push and pop.\n- Naive SMT manager that does not do any form of caching.\n- Optimal index selection.\n- Ability to interface with different solvers and set logic.\n\n### Changed\n\n- Accept .tsv fact files instead of .csv files.\n- Allow ML variables to be lowercase.\n- The `debugSmt` option logs SMT calls to separate files by solver, instead of\n  printing them to same stream.\n- Removed `is_valid_opt` and changed signature of `is_sat_opt` to take a list\n  of `bool smt` terms.\n\n### Fixed\n\n- Concurrency bug in the memoization of parameterized constructor symbols.\n- Bug in the recording of rule evaluation diagnostics.\n- Bug in the \"freshening\" of `let fun` expressions.\n\n## [0.3.0] - 2020-02-03\n\n### Added\n\n- Support for `fold` terms.\n- Options to restrict which results are printed after evaluation.\n- Nested, variable-capturing functions (i.e., `let fun` expressions).\n- Added generic serialization function `to_string`.\n- Preliminary (and undocumented) option to compile Formulog program to C++\n  instead of interpreting it.\n\n### Changed\n\n- Do not require parentheses around tuple types.\n- Do not reorder literals in rules (in order to preserve soundness of\n  flow-sensitive type checking).\n- Changed the names of some string-related built-in functions (`strcat` is now\n  `string_concat` and `strcmp` is now `string_cmp`).\n- Removed built-in function `string_of_i32`.\n\n### Fixed\n\n- Made Antlr parser faster by simplifying grammar.\n- Changed precedence of infix cons operator (i.e., `::`).\n- Added parameterized constructor symbols to fix type soundness issues arising\n  from the non-determinate type signatures of some formula constructors.\n- Made type checking flow-sensitive to soundly handle destruction of\n  user-defined constructors in formulas.\n\n## [0.2.0] - 2019-11-25\n\n### Added\n\n- Support wild card term `??` when \"invoking\" predicates as functions.\n- Constant array constructor `array_const` (from Z3's theory of arrays).\n- Ability to do partial magic set rewriting with annotations `@bottomup` and\n  `@topdown`.\n- Demand transformation simplification for magic set rewriting (following Tekle\n  and Liu [2010]).\n- Support for record types.\n- Support external input facts via annotation `@external`.\n- Support sequential runtime (for debugging) via `sequential` system property.\n- Support existential anonymous variables in negated atoms.\n\n### Changed\n\n- Increased the amount of information printed with the `debugMst` option.\n- Allow ML-style expressions to occur as logic programming terms.\n- Prefix names of automatically-generated ADT testers and getters with `#`.\n- Removed syntax highlighting for solver variables.\n- Don't require periods after declarations and function definitions.\n- Print thread name during SMT debugging.\n- Make sure that the same SMT call is never made twice (with the same timeout).\n\n### Fixed\n\n- Fixed bug with applying type substitutions that contain mappings to (possibly\n  nested) type variables.\n- Updated name of formula type in Vim syntax file.\n- Fixed a couple bugs in SMT-LIB parser.\n- Fixed bug with missing case in unification algorithm.\n- Boolean operators now short circuit.\n- Reject programs that use top-down rewriting in combination with IDB predicates\n  in the ML fragment.\n- Make sure that EDB relations are maintained during top-down rewriting, even\n  when they are only referenced in the ML fragment.\n\n## [0.1.0] - 2019-04-21\n\n### Added\n\n- Everything (initial release).\n"
  },
  {
    "path": "docs/Gemfile",
    "content": "source 'https://rubygems.org'\n\ngem \"jekyll\", \"~> 4.3.4\" # installed by `gem jekyll`\n# gem \"webrick\"        # required when using Ruby >= 3 and Jekyll <= 4.2.2\n\ngem \"just-the-docs\", \"0.10.0\" # pinned to the current release\n# gem \"just-the-docs\"        # always download the latest release\n"
  },
  {
    "path": "docs/_config.yml",
    "content": "title: Formulog \ndescription: Datalog for SMT-based static analysis \ntheme: just-the-docs\n\nurl: https://just-the-docs.github.io\n\naux_links:\n  GitHub Repository: https://github.com/HarvardPL/formulog\n"
  },
  {
    "path": "docs/eval_modes/compile.md",
    "content": "---\ntitle: Compilation\nlayout: page\nparent: Evaluation Modes\nnav_order: 3\n---\n\n# Compilation\n\nAs an alternative to being directly interpreted (the default), Formulog programs can be compiled into a mix of C++ and Souffle code, which can then in turn be compiled into an efficient executable.\nTo enable compilation, set the `--codegen` (`-c`) flag; generated code will be placed in the directory `./codegen/` (you can change this using the `--codegen-dir` option).\nWithin this directory you can use `cmake` to compile the generated code into a binary named `flg`.\n\nFor example, to compile and execute the `greeting.flg` program from above, you can use these steps:\n\n```\njava -jar formulog.jar -c examples/greeting.flg && \\\n  cd codegen && \\\n  cmake -B build -S . -DCMAKE_BUILD_TYPE=Release && \\\n  cmake --build build -j && \\\n  ./build/flg --dump-idb\n```\n\nThis should produce output like the following:\n\n```\nParsing...\nFinished parsing (0.000s)\nEvaluating...\nFinished evaluating (0.029s)\n\n==================== SELECTED IDB RELATIONS ====================\n\n---------- greeting (3) ----------\ngreeting(\"Hello, Bob\")\ngreeting(\"Hello, World\")\ngreeting(\"Hello, Alice\")\n```\n\nUse the command `./build/flg -h` see options available when running the executable.\n\nFor more information about the Formulog compiler, see the OOPSLA'24 paper [Making Formulog Fast: An Argument for Unconventional Datalog Evaluation](https://dl.acm.org/doi/10.1145/3689754) by Aaron Bembenek, Michael Greenberg, and Stephen Chong.\n\n## Dependencies\n\nTo build the generated code, you must have:\n\n- A C++ compiler that supports the C++17 standard (and OpenMP, if you want to produce a parallelized binary)\n- `cmake` (v3.21+)\n- [`boost`](https://www.boost.org/) (a version compatible with v1.81)\n- [`oneTBB`](https://oneapi-src.github.io/oneTBB/) (v2021.8.0 is known to work)\n- [`souffle`](https://souffle-lang.github.io/) (v2.3 is known to work; you have to use our [custom fork](https://github.com/aaronbembenek/souffle) if you want to combine compilation with [eager evaluation]({{ site.base_url }}{% link eval_modes/eager.md %}).)\n\nThe Formulog Docker image already has these dependencies installed."
  },
  {
    "path": "docs/eval_modes/eager.md",
    "content": "---\ntitle: Eager Evaluation\nlayout: page\nparent: Evaluation Modes\nnav_order: 4\n---\n\n# Eager Evaluation\n\nIn addition to traditional semi-naive Datalog evaluation, Formulog supports _eager evaluation_, a novel concurrent evaluation algorithm for Datalog that is faster than semi-naive evaluation on some Formulog workloads (often because it induces a more favorable distribution of the SMT workload across SMT solvers).\nWhereas semi-naive evaluation batches derived tuples to process them in explicit rounds, eager evaluation eagerly pursues the consequences of each tuple as it is derived.\n\nUsing eager evaluation with the Formulog interpreter is easy: just add the `--eager-eval` flag.\nEager evaluation can also be used with the Formulog compiler, provided you install [our custom version of Souffle](https://github.com/aaronbembenek/souffle).\nWhen you configure `cmake` on the generated code, you need to add `-DFLG_EAGER_EVAL=On`.\nFor example, to build a version of the greeting program that uses eager evaluation, use these commands:\n\n```\njava -jar formulog.jar -c examples/greeting.flg && \\\n  cd codegen && \\\n  cmake -B build -S . -DCMAKE_BUILD_TYPE=Release -DFLG_EAGER_EVAL=On && \\\n  cmake --build build -j && \\\n  ./build/flg --dump-idb\n```\n\nFor more information about eager evaluation, see the OOPSLA'24 paper [Making Formulog Fast: An Argument for Unconventional Datalog Evaluation](https://dl.acm.org/doi/10.1145/3689754) by Aaron Bembenek, Michael Greenberg, and Stephen Chong."
  },
  {
    "path": "docs/eval_modes/index.md",
    "content": "---\ntitle: Evaluation Modes\nlayout: page\nnav_order: 4\n---\n\n# Evaluation Modes\n\nGiven a Formulog program, there are many different ways to evaluate it.\nThese pages describe the primary options supported by our current implementation of Formulog."
  },
  {
    "path": "docs/eval_modes/options.md",
    "content": "---\ntitle: Options and System Properties \nlayout: page\nparent: Evaluation Modes\nnav_order: 1\n---\n\n# Options and System Properties \n\nFormulog evaluation is controlled by options and system properties.\nFor example, to interpret the test program with SMT logging and 2\nthreads, use the `debugSmt` property and `-j 2` option:\n\n```\njava -DdebugSmt -jar formulog.jar example/greeting.flg -j 2\n```\n\n## Options\n\nRun Formulog with the `-h` flag to see a list of the command-line options that are currently available.\nAs of Formulog v0.8.0, they are:\n\n```\nUsage: formulog [-chV] [--dump-all] [--dump-idb] [--dump-query] [--dump-sizes]\n                [--eager-eval] [--smt-stats] [--codegen-dir=<codegenDir>]\n                [-D=<outDir>] [-j=<parallelism>]\n                [--smt-solver-mode=<smtStrategy>]\n                [--dump=<relationsToPrint>]... [-F=<factDirs>]... <file>\nRuns Formulog.\n      <file>         Formulog program file.\n  -c, --codegen      Compile the Formulog program.\n      --codegen-dir=<codegenDir>\n                     Directory for generated code (default: './codegen').\n  -D, --output-dir=<outDir>\n                     Directory for .tsv output files (default: '.').\n      --dump=<relationsToPrint>\n                     Print selected relations.\n      --dump-all     Print all relations.\n      --dump-idb     Print all IDB relations.\n      --dump-query   Print query result.\n      --dump-sizes   Print relation sizes.\n      --eager-eval   Use eager evaluation (instead of traditional semi-naive\n                       Datalog evaluation)\n  -F, --fact-dir=<factDirs>\n                     Directory to look for fact .tsv files (default: '.').\n  -h, --help         Show this help message and exit.\n  -j, --parallelism=<parallelism>\n                     Number of threads to use.\n      --smt-solver-mode=<smtStrategy>\n                     Strategy to use when interacting with external SMT solvers\n                       ('naive', 'push-pop', or 'check-sat-assuming').\n      --smt-stats    Report basic statistics related to SMT solver usage.\n  -V, --version      Print version information and exit.\n```\n\n**Note:** Formulog does not print any results by default; use one of the\n`--dump*` options to print results to the console, or annotate intensional\ndatabase (IDB) relations with `@disk` to dump them to disk.\n\n## System Properties\n\nIn addition to options, there are many system properties that can be set using\nthe `-D` flag (as in `-DdebugSmt` or `-DuseDemandTransformation=false`). Some of\nthe most useful ones are:\n\n* `debugSmt` - log debugging information related to SMT calls to\n  the `solver_logs/` directory (defaults to false)\n* `debugMst` - print debugging information related to the magic set\n  transformation (defaults to false)\n* `debugRounds` - print statistics for each round of seminaive evaluation\n  (defaults to false)\n* `useDemandTransformation` - apply the demand transformation as a\n  post-processing step after the magic set transformation (defaults to true)\n* `softExceptions` - ignore exceptions during evaluation (i.e., treat them as\n  unification failures, and not as something that should stop evaluation;\n  defaults to false)\n* `sequential` - run interpreter without a thread pool (helpful for debugging\n  runtime; defaults to false)\n* `printRelSizes` - print final relation sizes (defaults to false)\n* `printFinalRules` - print the final, transformed rules (defaults to false)\n* `trackedRelations=REL_1,...,REL_n` - print facts from listed relations as they\n  are derived (defaults to the empty list)\n* `smtLogic=LOGIC` - set the logic used by the external SMT solver (defaults to\n  `ALL`)\n* `smtSolver=SOLVER` - set the external SMT solver to use; current options are\n  `z3` (default), `cvc4`, `yices`, and `boolector`\n* `smtDeclareAdts` - whether to declare Formulog algebraic data types to the SMT\n  solver upon initialization; set this to false for logics that do not support\n  ADTs (defaults to true)\n\n### Alternative SMT Solvers\n\nWhile we have primarily used Formulog with Z3 as the backend solver, we also\nhave some experimental (not recently tested) support for other solvers; not all\nthese solvers handle the full range of Formulog features. To use a solver\nbesides Z3, you need to set the `smtSolver` system property (see above).  For\neach solver, the relevant binary needs to be on your path: `z3` for Z3,\n`boolector` for Boolector, `cvc4` for CVC4, and `yices-smt2` for Yices."
  },
  {
    "path": "docs/eval_modes/smt.md",
    "content": "---\ntitle: Solver Modes and Incremental SMT Solving\nlayout: page\nparent: Evaluation Modes\nnav_order: 2\n---\n\n# Solver Modes and Incremental SMT Solving\n\nThe Formulog runtime associates one external SMT solver process per Formulog\nworker thread. Each SMT query is a list of conjuncts. If the SMT solver is\ninvoked via the `is_sat_opt` or `get_model_opt` function, this list is\nexplicitly given by the programmer; otherwise, if the solver is invoked via the\n`is_sat` or `is_valid` function, the Formulog runtime breaks the supplied\nproposition into conjuncts.\n\nFormulog supports three strategies for interacting with external SMT solvers;\nthey can be set using the `--smt-solver-mode` option.  Consider two SMT queries\n`x` and `y`, where `x` immediately precedes `y` and both are lists of conjuncts.\n\n- `naive`: reset the solver between queries `x` and `y` (do not use incremental\nSMT solving).\n- `push-pop`: try to use incremental SMT solving via the `push` and `pop` SMT\ncommands. This can work well when query `y` extends query `x`; e.g., `y = c ::\nx`, where `c` is an additional conjunct; this situation most commonly occurs\nwhen using [eager evaluation]({{ site.base_url }}{% link eval_modes/eager.md %}).\n- `check-sat-assuming`: try to use incremental SMT solving via the\n`check-sat-assuming` SMT command. This caches conjuncts in the SMT solver in a\nway such that they can be enabled or disabled per SMT query, and works well if\nthere are shared conjuncts between queries `x` and `y`, but query `x` is not\nsimply an extension of query `y` (e.g., it omits a conjunct in query `y`).\n\nFor more info, see the ICLP'20 extended abstract [Datalog-Based Systems Can Use Incremental SMT Solving](https://aaronbembenek.github.io/papers/datalog-incr-smt-iclp2020.pdf)\nby Aaron Bembenek, Michael Ballantyne, Michael Greenberg, and Nada Amin."
  },
  {
    "path": "docs/index.md",
    "content": "---\ntitle: Home\nlayout: home\nnav_order: 1\n---\n\n# Welcome to Formulog!\n\nFormulog is a programming language that extends the logic programming language Datalog with SMT solving and first-order functional programming.\nFormulog was designed to be a domain-specific language for implementing SMT-based program analyses, like refinement type checking and symbolic execution.\nBut who knows---maybe it will also have other uses!\n\nQuestions? Feedback? Please [raise a GitHub issue](https://github.com/HarvardPL/formulog/issues/new)!"
  },
  {
    "path": "docs/lang_ref/goal_directed_eval.md",
    "content": "---\ntitle: Goal-Directed Evaluation\nlayout: page\nparent: Language Reference\nnav_order: 3\n---\n\n# Goal-Directed Evaluation\n\nBy default, Formulog uses a standard bottom-up, saturating evaluation strategy.\nHowever, you can also trigger a form of goal-directed, top-down execution if you\ninclude a query in your Formulog program. A query is a rule with an empty head\nand a single (non-negated) body atom; for example, `:- p(X, \"a\").` is a query.\nEach program can only have a single query.\n\nThe Formulog runtime will use the query to rewrite your program using (a variant\nof) the magic set transformation technique. The rewritten program simulates\ntop-down evaluation when it is evaluated bottom-up. The rewriting happens after\ntype checking, but before program validation (i.e., before the checks described\nin the \"Program safety\" section are run). This means that you are allowed to\nwrite \"invalid\" Formulog programs, so long as that when they are rewritten, they\npass the validation checks. For example, this program is invalid evaluated\nbottom-up, since there are unbound variables (in the head of the rules):\n\n```\nrel member(i32, i32 list)\nmember(X, X :: _Xs).\nmember(X, _Y :: Xs) :- member(X, Xs).\n```\n\nHowever, when we add the query `:- member(_X, [1, 2, 3]).`, the program is\nrewritten to a valid program that can be evaluated bottom-up:\n\n```\ninput_member_fb(Xs) :- sup_0_0(_0, Xs).\ninput_member_fb([1, 2, 3]).\nmember(X, X :: Xs) :- input_member_fb(X :: Xs).\nmember(X, _0 :: Xs) :- sup_0_0(_0, Xs), member(X, Xs).\nsup_0_0(_0, Xs) :- input_member_fb(_0 :: Xs).\n```\n\nOur magic set transformation technique guarantees that if the original program\nmeets the requirements of stratified negation, then the rewritten program will\nalso be stratified. However, the transformation can turn a program that\nterminates into a program that does not terminate. Also, it cannot be used with\npredicates that are \"invoked\" from the functional fragment of Formulog.\n\n## Query Syntax\n\nQueries are in the form `:- A.` where `A` is a positive (non-negated) atom. The\ntypical rules about variable usage apply (see the \"Anonymous variables\" section\nof the [Program Safety page]({{ site.base_url }}{% link lang_ref/program_safety.md %})). If you want to have a query consisting of\nmultiple atoms, write a rule defining a new relation and then query that\nrelation. For example, the hypothetical query `:- A, B.` could be rewritten as\nthe rule `R :- A, B.` and query `:- R.`. There can be only one query per\nprogram.\n\n## Partial Goal-Directed Evaluation\n\nIt is also possible to only use goal-directed evaluation for part of a program.\nYou can control this by annotating IDB relation declarations with\n`@bottomup` and `@topdown`:\n\n```\n@bottomup\nrel foo(i32, string)\n\n@topdown\nrel bar(i32, i32, string)\n```\n\nA relation annotated as `@bottomup` will always be evaluated bottom-up (i.e., in\nan exhaustive fashion), and a relation annotated as `@topdown` will always be\nevaluated top-down (i.e., in a goal-directed fashion). These annotations can be\nused whether or not a top-level query is supplied, and there are no restrictions\non how bottom-up and top-down relations can interact with each other (outside of\nthe normal restriction of stratified negation). Furthermore, not every relation\nneeds to be annotated. An unannotated relation will be evaluated bottom-up in\nthe absence of a top-level query, and top-down otherwise.\n"
  },
  {
    "path": "docs/lang_ref/index.md",
    "content": "---\ntitle: Language Reference\nlayout: page\nnav_order: 5\n---\n\n# Language Reference\n\nThis set of documents describes the Formulog language (as opposed to its current implementation).\nPlease raise a [GitHub issue](https://github.com/HarvardPL/formulog/issues/new) if anything is unclear, incomplete, or incorrect :)\n\nFor an overview of Formulog and its motivations, we recommend checking out our [publications]({{ site.base_url }}{% link pubs.md %})."
  },
  {
    "path": "docs/lang_ref/lang_basics.md",
    "content": "---\ntitle: Language Basics\nlayout: page\nparent: Language Reference\nnav_order: 1\n---\n\n# Language Basics\n\nFormulog is an extension of Datalog designed to support program analyses that\nuse logical formulas, such as symbolic execution and refinement type checking.\n\nA Formulog program consists of three main components:\n\n* type definitions;\n* relation declarations and definitions; and\n* function definitions.\n\n## Types\n\nFormulog has a strong, static type system.\n\n### Built-in Types\n\nFormulog has five built-in primitive types:\n\n* booleans (`bool`), i.e., `true` and `false`;\n* signed 32-bit integers (`i32` or equivalently `bv[32]`), as in `42`;\n* signed 64-bit integers (`i64` or equivalently `bv[64]`), as in `42L`;\n* 32-bit floating point numbers (`fp32` or equivalently `fp[8,24]`), as in\n  `42.0F`;\n* 64-bit floating point numbers (`fp64` or equivalently `fp[11,53]`), as\n  in `42.0`\n  or `42.0D`; and\n* string constants (`string`), as in `\"hello\"`.\n\nBeyond these primitive types, Formulog provides the following built-in algebraic\ndata types:\n\n```\ntype 'a list =\n  | nil\n  | cons('a, 'a list)\n\ntype 'a option =\n  | none\n  | some('a)\n\ntype cmp =\n  | cmp_lt\n  | cmp_eq\n  | cmp_gt\n```\n\nIt also has built-in types representing logical formulas, but a discussion of\nthese is delayed until the [section on logical formulas]({{ site.base_url }}{% link lang_ref/logical_formulas.md %}).\n\n#### List Notation\n\nFormulog provides special notation for terms of the `list` type. The `cons`\nconstructor can be written using the infix notation `::`; i.e., `X :: Y` is\nshorthand for `cons(X, Y)`. A list can also be written as a sequence of\ncomma-separated elements between a pair of square brackets. For example, the\nterm `[X, Y, Z]` is shorthand for `cons(X, cons(Y, cons(Z, nil)))`. The term\n`[]` is `nil`, the empty list.\n\nBoth notations can be used in pattern matching (described below).\n\n### User-Defined Types\n\nFormulog allows users to define their own (polymorphic) algebraic data types.\nFor instance, this defines a list-like type:\n\n```\ntype 'a my_list =\n  | my_nil\n  | my_cons('a, 'a my_list)\n```\n\nLike types, constructors must begin with a lowercase letter.\n\nFormulog also has support for tuple types, such as the type `i32 * string`, and\nusers can also define type aliases, such as this one that defines a map to be an\nassociation list:\n\n```\ntype ('k, 'v) map = ('k * 'v) my_list\n```\n\nMutually recursive types are written using `and`:\n\n```\ntype foo =\n  | foo1\n  | foo2(bar)\nand bar =\n  | bar1(foo)\n  | bar2(bar, bar)\n```\n\nYou can also define records, as here:\n\n```\ntype 'a linked_list = {\n  val  : 'a;\n  next : 'a linked_list option;\n}\n```\n\nLabels must be valid identifiers and cannot be shared across other types. For\neach label, Formulog will automatically generate a function with that name that\nextracts the relevant value from the record. For example, in the case of\n`linked_list`, Formulog will generate:\n\n```\nval  : 'a linked_list -> 'a\nnext : 'a linked_list -> 'a linked_list option\n```\n\nFormulog also supports OCaml-style functional record update, as in this example:\n\n```\ntype point3d = { x : i32; y : i32; z : i32 }\nfun foo : i32 =\n  let X = { x = 1; y = 2; z = 3 } in\n  let Y = { X with x = -1; z = 0 } in\n  x(Y) + y(Y) + z(Y)\n```\n\nA call `foo` would evaluate to the value `1`.\n\n## Relations\n\nIn Formulog, relations are declared using the keyword `rel`, followed by the\nname of the relation, and the types of the relation arguments. Relation\narguments can also be given labels (as documentation):\n\n```\nrel foo(i32, string)\nrel pair(first: i32, second: i32) (* `first` and `second` are labels *)\n```\n\nSome relations are defined only in terms of explicitly enumerated facts. This\none relates pairs of nodes and consists of three pairs:\n\n```\ntype node = string\nrel edge(node, node)\nedge(\"a\", \"b\").\nedge(\"b\", \"c\").\nedge(\"c\", \"b\").\n```\n\nRelations like this—that consist only of enumerated facts—can be annotated with\nthe `@edb` annotation, which tells Formulog to treat them as part of the\nextensional database (EDB).\n\n```\n@edb rel edge(node, node)\n```\n\nFormulog assumes that every relation not annotated with `@edb` is an intensional\ndatabase (IDB) relation, meaning that it is defined by rules and should be\ntreated as an output of the program. For instance, this predicate computes\ntransitive closure over the previously defined `edge` predicate:\n\n```\nrel tc(node, node)\ntc(X, Y) :- edge(X, Y).\ntc(X, Z) :- tc(X, Y), edge(Y, Z).\n```\n\nA Formulog rule consists of a list of head atoms (the atoms to the left of the\n`:-`) and a list of body atoms (the atoms to the right of the `:-`). An atom is\neither a nullary predicate symbol (i.e., a predicate that takes no arguments) or\na n-ary predicate symbol followed by a parenthesized, comma-separated list of\nterms. Each term is either\n\n* a primitive like `42`;\n* a variable like `X`;\n* a constructed term like `some(X :: [2, 3])`;\n* a term of the form `t not C`, where `t` is a term and `C` is a constructor\n  symbol (this evaluates to `true` if the outermost constructor of `t` is not\n  `C`); or\n* a function call to a user-defined or built-in function like `i32_to_i64(42)`\n  (functions are described in the next section).\n\nAdditionally, atoms in the body of a rule can be negated, as in the atom `!tc(X,\n\"c\")`. Restrictions on the use of negation will be described later in this\nguide.\n\nFormulog also has two built-in binary predicates, `=` and `!=`:\n\n```\nrel ok\nok :- X = \"hello\", X != \"goodbye\".\n```\n\nThe first of these predicate is true when its arguments unify to the same term,\nand the second is true when its arguments cannot be unified.\n\nFinally, any Formulog term of type `bool` can be used in place of an atom in the\nrule body, as here:\n\n```\nrel foo(bool)\noutput p\np :- foo(X), X.\n```\n\nwhere the rule is translated to\n\n```\np :- foo(X), X = true.\n```\n\n### Reading EDB Relations from Disk\n\nIt is possible to specify that an EDB relation should be read from an external\nfile by annotating its declaration with `@disk`, as in\n\n```\n@disk @edb foo(i32, i32, string list)\n\n@disk @edb bar(string)\n```\n\nThe Formulog runtime will look in the directories specified on the command line\nfor files called`foo.tsv` and `bar.tsv` (defaulting to the current directory).\nAs suggested by the `.tsv` extension, these files should contain rows of\ntab-separated terms, where each row corresponds to one input fact, and each\ncolumn corresponds to an argument position.\n\nSo, a file `foo.tsv` might look like this\n\n```\n42\t0\t[\"x\"]\n24\t1\t[\"\", \" \"]\n100\t-1\t[]\n```\n\n(note that the terms on a line are separated by tabs, *not* spaces); it would\ncorrespond to the facts\n\n```\nfoo(42, 0, [\"x\"]).\nfoo(24, 1, [\"\", \" \"]).\nfoo(100, -1, []).\n```\n\nSimilarly, a `bar.tsv` file looking like this\n\n```\n\"hello\"\n\"goodbye\"\n\"ciao\"\n\"aloha\"\n```\n\nwould correspond to the facts\n\n```\nbar(\"hello\").\nbar(\"goodbye\").\nbar(\"ciao\").\nbar(\"aloha\").\n```\n\nEvery fact directory must have a `.tsv` file for _every_ external input\nrelation (the file can be empty).\n\n### Writing IDB Relations to Disk\n\nAn IDB relation can be annotated with the annotation `@disk`, in which case\nFormulog will dump its contents into a `.tsv` file in the directory specified on\nthe command line (defaulting to the current directory).\n\nFor example, the program\n\n```\nrel bar(i32, i32)\nbar(1, 2).\nbar(3, 4).\n\n@disk rel foo(i32, i32)\nfoo(X, Y) :- bar(X, Y).\n```\n\nwill result in a file `foo.tsv` with the tab-separated contents:\n\n```\n1\t2\n3\t4\n```\n\n## Functions\n\nFormulog allows users to define ML-style functions, that can then be invoked\nfrom within Datalog-style rules. These functions can be polymorphic, but cannot\nbe higher-order. The functions must have explicit type annotations. For example,\nhere is a function for finding the nth element of a list:\n\n```\nfun nth(Xs : 'a list, N : i32) : 'a option =\n  match Xs with\n  | [] => none\n  | X :: Xs =>\n    if N < 0 then none\n    else if N = 0 then some(X)\n    else nth(Xs, N - 1)\n  end\n```\n\nNo special syntax is required for defining recursive functions, although\nmutually recursive functions must be defined with `and`, as here:\n\n```\nfun neg_abs(X: i32) = if X > 0 then -X else X\n\nfun is_even(X: i32) : bool =\n  let X = neg_abs(X) in \n  X = 0 || is_odd(X + 1)\nand is_odd(X: i32) : bool =\n  let X = neg_abs(X) in\n  X != 0 && is_even(X + 1)\n```\n\nWe support some of the basic ML syntax constructions, like `match` and `let`.\nHowever, you will find Formulog's syntax to be less flexible than most ML\nimplementations; for example, `some(X)` is okay but `some X` is not.\n\nDespite the fact that we do not support higher-order functions, we do support\nnested functions that can locally capture variables and we also support a\nspecial parameterized term `fold`:\n\n```\nfold[f] : ['a, 'b list] -> 'a\n```\n\nwhere `f` is the name of a function of type `['a, 'b] -> 'a`. Here's an example\nusing both nested functions and `fold`:\n\n```\nfun rev(Xs: 'a list) : 'a list =\n  let fun cons_wrapper(Xs: 'a list, X: 'a) : 'a list = X :: Xs in\n  fold[cons_wrapper]([], Xs)\n```\n\nTop-level nullary functions (i.e., ones that take no arguments) can be\nintroduced with the keyword `const` instead of `fun`:\n\n```\nconst pi : fp64 = 3.14\n(* same as `fun pi : fp64 = 3.14` *)\n```\n\n### Lifted Relations and Aggregation\n\nFormulog allows any relation (i.e., EDB relations, IDB relations, and the\nbuilt-in relations `!=` and `=`) to be lifted to a boolean-returning function.\nFor instance, we can write code like this:\n\n```\noutput bar(i32)\n\nfun foo(N:i32) : bool = bar(N + 1)\n```\n\nHere, the function `foo(n)` returns `true` whenever the `bar` relation contains\n`n + 1`.\n\nFormulog supports aggregation through the wild card term `??`, which can be used\nas an argument when \"invoking\" a relation as a function. For example, given the\nrelation `p` that relates a `bool` to an `i32`, we have:\n\n* `p(true, 42)` returns a boolean (whether `true` is related to `42`)\n* `p(true, ??)` returns a list of `i32` terms (the ones that are related\n  to `true`)\n* `p(??, 42)` returns a list of `bool` terms (the ones that are related to `42`)\n* `p(??, ??)` returns a list of pairs constituting the relation\n\nThe use of lifted predicates must be stratified, as described in the \"Program\nSafety\" document.\n\n### Built-in Functions\n\nFinally, Formulog already has a bunch of basic functions built-in (mostly to do\nwith manipulating primitives):\n\n* functions for basic mathematical operations for types `i32`, `i64`, `fp32`,\n  and `fp64`:\n    * addition (`*_add`), as in `fp32_add`\n    * subtraction (`*_sub`)\n    * multiplication (`*_mul`)\n    * negation (`*_neg`)\n* bit vector operations for types `i32` and `i64`:\n    * bitwise and (`*_and`),\n    * bitwise or (`*_or`)\n    * bitwise exclusive or (`*_xor`), for types `i32` and `i64`;\n    * signed division (`*_sdiv`)\n    * signed remainder (`*_srem`)\n    * unsigned division (`*_udiv`)\n    * unsigned remainder (`*_urem`)\n    * shift left (`*_shl`)\n    * logical shift right (`*_lshr`)\n    * arithmetic shift right (`*_ashr`)\n* float operations for types `fp32` and `fp64`:\n    * equality (`*_eq`; this is floating point equality, as opposed to\n      structural equality via the predicate `=`);\n    * division (`*_div`)\n    * remainder (`*_rem`)\n* Comparison operations `*_lt`, `*_le`, `*_gt`, `*_ge` for types `i32`, `i64`,\n  `fp32`, and `fp64` (the bit vector ones are implicitly for signed comparison)\n* Signed compare (`*_scmp`) and unsigned compare (`*_ucmp`) operators for types\n  `i32` and `i64`; these return a term of type `cmp` (described above)\n* boolean operators `!`, `&&`, and `||`\n* numeric primitive conversion operations, in the form `*_to_*` (e.g.,\n  `i32_to_fp64`)\n* the operators `string_to_i32` and `string_to_i64`, which convert the string\n  representation of an integer to a term of type `i32 option` and `i64 option`,\n  respectively. The string should either be a decimal integer preceded\n  optionally by `-` or `+`, or a hexadecimal integer preceded by `0x`. The\n  operations return `none` if the string is not in the proper format or\n  represents an integer of too great magnitude to fit in 32/64 bits.\n\nStandard arithmetic notation can be used for signed `i32` operations. For\nexample, `38 + 12 / 3` is shorthand for `i32_add(38, i32_sdiv(12, 3))`.\n\nFormulog supplies some `string` manipulation functions:\n\n```\nstring_concat      : [string, string] -> string\nstring_cmp         : [string, string] -> cmp\nstring_matches     : [string, string] -> bool\nstring_starts_with : [string, string] -> bool\nsubstring          : [string, i32, i32] -> string option\nstring_length      : [string] -> i32\nchar_at            : [string] -> i32 option\nstring_to_list     : [string] -> i32 list\nlist_to_string     : [i32 list] -> string\nto_string          : 'a -> string\n```\n\nThe function `string_matches` returns `true` when its first argument matches its\nsecond argument, which can be a regular expression. The function\n`string_starts_with` returns `true` when its second argument is a prefix of its\nfirst argument. The function `substring(s, i, j)` extracts the substring of `s`\nstarting at index `i` and ending at index `j - 1`; it returns `none` for\ninappropriate `i` or `j`.\n\nThe functions `char_at`, `string_to_list`, and `list_to_string` can be used to\ntreat a string as a list of characters. We represent characters as terms of\ntype `i32`. We currently do not guarantee any particular behavior if an integer\nless than 0 or greater than 255 is used as a character.\n\nFinally, for the purposes of debugging, Formulog supplies a `print` function of\ntype `'a -> bool`; it always evaluates to `true`.\n"
  },
  {
    "path": "docs/lang_ref/logical_formulas.md",
    "content": "---\ntitle: Logical Formulas\nlayout: page\nparent: Language Reference\nnav_order: 4\n---\n\n# Logical Formulas\n\nFormulog provides support for representing and reasoning about logical formulas.\n\n## Example\n\nThis example program would produce the facts `ok1`, `ok2`, and `ok3`.\n\n```\nrel ok1\nok1 :-\n  is_valid(`false ==> true`),\n  !is_sat(`true ==> false`).\n\nrel ok2\nok2 :-\n  E = `bv_add(#x[bv[32]], 42) #= 0`,\n  is_sat(E),\n  F = `bv_sge(#x[bv[32]], 0)`,\n  is_sat(F),\n  !is_sat(`E /\\ F`).\n\ntype 'a my_list =\n  | my_nil\n  | my_cons('a, 'a my_list)\n\nrel ok3\nok3 :-\n  Xs = #xs[bool my_list],\n  Ys = #ys[bool my_list],\n  E = `Xs #= my_cons(true, Ys)`,\n  is_sat(E),\n  F = `#is_my_nil(Xs)`,\n  is_sat(F),\n  !is_sat(`E /\\ F`).\n```\n\n## Formula Types\n\nFor every non-formula type τ, there are two corresponding types that are used to\nrepresent τ-valued logical formulas. The first is `τ smt`, which represents a\nτ-valued formula. The second is `τ sym`, which represents a τ-valued formula\nvariable (it is sometimes helpful to distinguish between these two types).\n\nYou will often see formulas quoted with backticks, as in\n\n```\n`#x[bool] #= true`\n```\n\nQuoted terms are type checked differently than terms outside of quotations.\nOutside of quotations, the types τ, `τ smt`, and `τ sym` are treated as being\ndistinct. However, in quoted terms, they are treated as being all the same type.\nThis bimodal type checking makes it easy to write expressive formulas, while\nmaking sure that evaluation outside of formulas does not get stuck, which might\nhappen if a boolean formula were passed to a function expecting a concrete\nboolean argument.\n\nThe Formulog type sensitive is flow-sensitive, in that the order of the atoms\nand terms in a rule affect whether that rule is considered well typed or not.\nFor example, it rejects the first rule in this program and not the second, even\nthough they are logically equivalent:\n\n```\nrel p(bool smt)\nrel q(bool)\nrel not_ok\nrel ok\n\nnot_ok :- p(`X`), q(X).\nok     :- q(X), p(`X`).\n```\n\nIt rejects the first rule because, given a left-to-right reading of the rule,\n`X` is bound in a position that has type `bool smt`, and so there is no\nguarantee it is a concrete `bool` (which would be required for it to be a member\nof `q`). The second rule is fine since `X` is bound in a position that has type\n`bool`, which becomes a `bool smt` when it is quoted as an argument to `p`. The\ntype checker currently uses the order that the rule was originally written; in\nthe future, the type checker could try to reorder rules to make them well typed.\n\n### Uninterpreted Sorts\n\nFormulog allows users to define uninterpreted sorts that can be used within\nformulas. For instance, you can declare a polymorphic uninterpreted sort like\nthis:\n\n```\nuninterpreted sort ('a, 'b) foo\n```\n\n## Representing Formulas\n\nFormulas are constructed from a library of constructors and are typically quoted\nby backticks, which tells the type checker to use the \"formula mode\" of the\nbimodal type system. Function calls that take arguments cannot appear within\nquotations.\n\n### Formula variables\n\nFormulog distinguishes between logic programming variables (like `X`) and\nformula variables (like `#x[bool]`). The latter are only interpreted as\nvariables within formula; otherwise they are ground terms. Formula variables can\nbe created using a special syntax: a pound sign, followed by a term `t`\nof arbitrary type within curly braces, followed by a type τ within square\nbrackets, as in\n\n```\n#{t}[τ]\n```\n\nτ cannot have any type variables and cannot contain any formula types (like\n`sym` or `smt`). The term `t` is the \"name\" of the formula variable.\n\nA formula variable will unify with another formula variable only if they have\nthe same \"name\" and type. For example,\n\n```\n#{42}[bool] = #{\"hello\"}[bool]\n```\n\nnever holds, although the formula\n\n```\n`#{42}[bool] #= #{\"hello\"}[bool]`\n```\n\nis satisfiable (where `#=` is the notation for formula equality). **Note:** A\nformula variable `#{t}[τ]` is guaranteed to not unify with any subterm of `t`;\nthat is, it is fresh for `t`.\n\nThere is also a shorthand syntax: a pound sign, followed by an identifier,\nfollowed by a type within square brackets, as in `#x[bool]`. This is the same\nthing as `#{\"x\"}[bool]`.\n\n### Built-in Formula Terms\n\nFormulog provides built-in terms that are used to construct formulas that should\nbe interpreted under a particular theory. For the most part, these constructors\ndirectly reflect the SMT-LIB standard.\n\n#### Parameterized Terms\n\nYou will see that many formula terms are parameterized with a type or natural\nnumber. For example, the constructor for formula equality, `smt_eq[τ]`, is\nparameterized with the type τ of the operands of the equality, and the\nconstructor for a bit vector constant, `bv_const[k]`, is parameterized with the\nwidth `k` of the bit vector. These parameters are necessary either because the\ntype information is important to have at runtime (when serializing formulas into\nSMT-LIB), or for type safety reasons (issues arise if the type of the term does\nnot uniquely determine the types of its arguments). However, Formulog can often\ninfer the correct type without an explicit annotation. If you leave out the\nsquare brackets, Formulog will try to infer every parameter for that term;\nalternatively, select parameters can be inferred by using the wildcard parameter\n`?`. For example, these formulas all say the same thing:\n\n```\n`smt_eq[bv[32]](bv_const[32](42), 0)`\n`smt_eq[?](bv_const[?](42), 0)`\n`smt_eq(bv_const(42), 0)`\n```\n\nFormulog can infer that this is a comparison of 32-bit bit vectors from the fact\nthat the second operand is the constant `0`, which has type `bv[32]`. However,\nthe following formulas are unacceptable, since Formulog cannot infer the widths\nof the bit vectors in the comparisons:\n\n```\n`smt_eq[?](bv_const[?](42), bv_const[?](0))`\n`smt_eq(bv_const(42), bv_const(0))`\n```\n\nIn this case, one annotation is enough to clarify things:\n\n```\n`smt_eq[bv[32]](bv_const(42), bv_const(0))`\n`smt_eq(bv_const[32](42), bv_const(0))`\n```\n\nThe parameters to a parameterized constructor have to be fully resolved at\ncompile time.\n\n#### Logical Connectives\n\nFormulog has the standard first-order logic connectives:\n\n```\nsmt_not    : bool smt -> bool smt\nsmt_eq[τ]  : [τ smt, τ smt] -> bool smt\nsmt_and    : [bool smt, bool smt] -> bool smt\nsmt_or     : [bool smt, bool smt] -> bool smt\nsmt_imp    : [bool smt, bool smt] -> bool smt\nsmt_ite    : [bool smt, 'a smt, 'a smt] -> 'a smt\nsmt_let[τ] : [τ sym, τ smt, 'a smt] -> 'a smt\nsmt_exists : [smt_wrapped_var list, bool smt, smt_pattern list list] -> bool smt\nsmt_forall : [smt_wrapped_var list, bool smt, smt_pattern list list] -> bool smt\n```\n\nThe quantifiers deserve some explanation. The first argument is a list of\n\"wrapped\" formula variables bound by the quantifier; the type `smt_wrapped_var`\nhas a single constructor:\n\n```\nsmt_wrap_var[τ] : τ sym -> smt_wrapped_var\n```\n\nThe second argument is the body of the quantifier. The third and final argument\nrepresents a list of patterns to supply for trigger-based quantifier\ninstantiation. Each member of the outermost list represents a single pattern,\npossibly consisting of multiple terms. The type `smt_pattern` has a single\nconstructor:\n\n```\nsmt_pat[τ] : τ -> smt_pattern\n```\n\nHowever, it is unlikely that you will have to use these constructors directly,\nas we supply notation that should cover most situations:\n\n```\n~ ...                     (* negation *) \n... #= ...                (* equality *)\n... /\\ ...                (* conjunction *) \n... \\/ ...                (* disjunction *)\n... ==> ...               (* implication *)\n... <==> ...              (* iff *)\n#if ... then ... else ...\n#let ... = ... in ... \n```\n\nThe binary operators are listed above in order of precedence (so `#=`\nbinds the most tightly, and `<==>` the least tightly). They all associate to the\nright, except for `#=`, which associates to the left.\n\nThere is also notation for the quantifiers `forall` and `exists`, as in this\nexample:\n\n```\n  `forall #x[bool]. exists #y[bool]. #x[bool] #= ~#y[bool]`\n```\n\nThe notation supports binding multiple variables:\n\n```\n  `exists #x[bool], #y[bool]. #x[bool] #= ~#y[bool]`\n```\n\nYou can also specify patterns for trigger-based instantiation. For example, say\nthat `f` is an uninterpreted function from bools to bools. This formula says\nthat `f(x) = x` for all `x`, using `f(x)` as a trigger:\n\n```\n  `forall #x[bool] : f(#x[bool]). f(#x[bool]) #= #x[bool]`\n```\n\nThe notation supports patterns with multiple terms (they should be separated by\ncommas); however, it does not support multiple patterns, in which case you need\nto use the explicit constructor described above.\n\n#### Bit Vectors\n\nWe have bit vectors, where `bv[k] smt` is a `k`-bit symbolic bit vector:\n\n```\nbv_const[k]            : bv[32] -> bv[k] smt\nbv_big_const[k]        : bv[64] -> bv[k] smt\nbv_neg                 : bv[k] smt -> bv[k] smt\nbv_add                 : [bv[k] smt, bv[k] smt] -> bv[k] smt\nbv_sub                 : [bv[k] smt, bv[k] smt] -> bv[k] smt\nbv_mul                 : [bv[k] smt, bv[k] smt] -> bv[k] smt\nbv_sdiv                : [bv[k] smt, bv[k] smt] -> bv[k] smt\nbv_srem                : [bv[k] smt, bv[k] smt] -> bv[k] smt\nbv_and                 : [bv[k] smt, bv[k] smt] -> bv[k] smt\nbv_or                  : [bv[k] smt, bv[k] smt] -> bv[k] smt\nbv_xor                 : [bv[k] smt, bv[k] smt] -> bv[k] smt\nbv_to_bv_signed[j,k]   : bv[j] smt -> bv[k] smt\nbv_to_bv_unsigned[j,k] : bv[j] smt -> bv[k] smt\nfp_to_sbv[i,j,k]       : fp[i,j] smt -> bv[k] smt\nfp_to_ubv[i,j,k]       : fp[i,j] smt -> bv[k] smt\nbv_slt[k]              : [bv[k] smt, bv[k] smt] -> bool smt\nbv_sle[k]              : [bv[k] smt, bv[k] smt] -> bool smt\nbv_sgt[k]              : [bv[k] smt, bv[k] smt] -> bool smt\nbv_sge[k]              : [bv[k] smt, bv[k] smt] -> bool smt\nbv_ult[k]              : [bv[k] smt, bv[k] smt] -> bool smt\nbv_ule[k]              : [bv[k] smt, bv[k] smt] -> bool smt\nbv_ugt[k]              : [bv[k] smt, bv[k] smt] -> bool smt\nbv_uge[k]              : [bv[k] smt, bv[k] smt] -> bool smt\nbv_extract[j,k]        : [bv[j] smt, i32, i32] -> bv[k] smt\nbv_concat[i,j,k]       : [bv[i] smt, bv[j] smt] -> bv[k] smt\n```\n\nNote that in some cases the bit vector width is a parameter to the constructor;\nas noted previously, parameters can often be inferred.\n\nThe `bv_extract` and `bv_concat` constructors currently do not enforce some\nconstraints on their parameters (for example, with `bv_concat[i,j,k]`, it must\nbe that `i + j = k`). Illegal parameter choices are therefore not caught by the\ntype system, and might lead to SMT solver crashes at runtime.\n\n#### Floating Point\n\nAnd we have floating point, where `fp[j,k] smt` is a symbolic floating point\nnumber with exponent length `j` and signficand length `k`:\n\n```\nfp_const[j,k]     : fp[32] -> fp[j,k] smt\nfp_big_const[j,k] : fp[64] -> fp[j,k] smt\nfp_neg            : fp[j,k] smt -> fp[j,k] smt\nfp_add            : [fp[j,k] smt, fp[j,k] smt] -> fp[j,k] smt\nfp_sub            : [fp[j,k] smt, fp[j,k] smt] -> fp[j,k] smt\nfp_mul            : [fp[j,k] smt, fp[j,k] smt] -> fp[j,k] smt\nfp_div            : [fp[j,k] smt, fp[j,k] smt] -> fp[j,k] smt\nfp_rem            : [fp[j,k] smt, fp[j,k] smt] -> fp[j,k] smt\nfp_to_fp[h,i,j,k] : fp[h,i] smt -> fp[j,k] smt\nbv_to_fp[i,j,k]   : bv[i] smt -> fp[j, k] smt\nfp_lt[j,k]        : [fp[j,k] smt, fp[j,k] smt] -> bool smt\nfp_le[j,k]        : [fp[j,k] smt, fp[j,k] smt] -> bool smt\nfp_gt[j,k]        : [fp[j,k] smt, fp[j,k] smt] -> bool smt\nfp_ge[j,k]        : [fp[j,k] smt, fp[j,k] smt] -> bool smt\nfp_is_nan[j,k]    : fp[j,k] smt -> bool smt\nfp_eq[j,k]        : [fp[j,k] smt, fp[j,k] smt] -> bool smt\n```\n\nTo make it syntactically more pleasant to deal with common floating point types,\ninstead of supplying both the exponent and significand length, users can supply\na single parameter that is expanded into the appropriate exponent and\nsignificand:\n\n* the parameter `16` is expanded to `5,11`;\n* the parameter `32` is expanded to `8,24`;\n* the parameter `64` is expanded to `11,53`; and\n* the parameter `128` is expanded to `15,113`.\n\nFor example, the term `fp_const[32](...)` is equivalent to\n`fp_const[8,24](...)`.\n\n#### Integers\n\nMathematical integers are represented by the type `int smt`:\n\n```\nint_abs       : int smt -> int smt\nint_neg       : int smt -> int smt\nint_const     : bv[32] -> int smt\nint_big_const : bv[64] -> int smt\nint_ge        : [int smt, int smt] -> bool smt\nint_gt        : [int smt, int smt] -> bool smt\nint_le        : [int smt, int smt] -> bool smt\nint_lt        : [int smt, int smt] -> bool smt\nint_add       : [int smt, int smt] -> int smt\nint_mul       : [int smt, int smt] -> int smt\nint_mod       : [int smt, int smt] -> int smt\nint_sub       : [int smt, int smt] -> int smt\nint_div       : [int smt, int smt] -> int smt\nint_to_bv[k]  : int smt -> bv[k] smt\nbv_to_int[k]  : bv[k] smt -> int smt\n```\n\nNote that `int_to_bv[k]` and `bv_to_int[k]` do not correspond to any operations\nin the SMT-LIB standard. These are serialized as `int2bv` and `bv2int`\noperations, which are supported by Z3, but not by some other solvers.\n\n#### Arrays\n\nArrays from `'a` to `'b` are represented by the type `('a, 'b) array smt`:\n\n```\narray_select[τ]  : [(τ, 'a) array smt, τ smt] -> 'a smt \narray_store      : [('a, 'b) array smt, 'a smt, 'b smt] -> ('a, 'b) array smt\narray_default[τ] : (τ, 'a) array smt -> 'a smt\narray_const      : 'b smt -> ('a, 'b) array smt\n```\n\n#### Strings\n\nSymbolic strings are represented by the type `string smt`:\n\n```\nstr_at       : string smt, int smt -> string smt\nstr_concat   : string smt, string smt -> string smt\nstr_contains : string smt, string smt -> bool smt\nstr_indexof  : string smt, string smt, int smt -> int smt\nstr_len      : string smt -> int smt\nstr_prefixof : string smt, string smt -> bool smt\nstr_replace  : string smt, string smt, string smt -> string smt\nstr_substr   : string smt, int smt, int smt -> string smt\nstr_suffixof : string smt, string smt -> bool smt\n```\n\nThese operations follow the theory of strings supported by Z3 and CVC4.\n\n### Using Algebraic Data Types and Records in Formulas\n\nTerms constructed from user-defined algebraic data types can also be used in\nformulas, where they are interpreted under the theory of algebraic data types.\nSay we define this type:\n\n```\ntype 'a my_list =\n  | my_nil\n  | my_cons('a, 'a my_list)\n```\n\nIn addition to being able to use the constructors `my_nil` and `my_cons` within\na formula, one can also use constructors that are automatically generated by\nFormulog and that make it easier to state formulas about `my_list` terms:\n\n```\n#is_my_nil  : 'a my_list smt -> bool smt\n#is_my_cons : 'a my_list smt -> bool smt\n#my_cons_1  : 'a my_list smt -> 'a smt\n#my_cons_2  : 'a my_list smt -> 'a my_list smt\n```\n\nThese automatically generated constructors fall into two categories: testers,\nwhich are identified by the prefix `#is_` followed by the name of the\nconstructor and are used to test the outermost constructor of a (possibly\nsymbolic) term, and getters, which are identified by the name of the constructor\nprefixed with a `#` and followed by an underscore and an argument position, and\nwhich are used to extract the argument of a (possibly symbolic)\nterm.\n\nSymbolic getters are also created for records, where each getter is the name of\nthe relevant label prefixed with `#`.\n\n### Uninterpreted Functions\n\nFormulog also provides a way to declare uninterpreted functions, as here:\n\n```\nuninterpreted fun foo(bv[32] smt) : bool smt\n```\n\nThis effectively defines a new constructor for `bool smt` that expects a single\nargument of type `bv[32] smt`. Uninterpreted functions can only be used within\nformulas.\n\n## Reasoning about Formulas\n\nFormulog currently provides these functions for reasoning about/manipulating\nformulas:\n\n```\nis_sat       : bool smt -> bool\nis_sat_opt   : [bool smt list, i32 option] -> bool option\nis_valid     : bool smt -> bool\nget_model    : [bool smt list, i32 option] -> model option\nquery_model  : ['a sym, model] -> 'a option\n```\n\nThe functions `is_sat` and `is_valid` check the satisfiability and validity,\nresp., of their argument and throw an exception if the SMT solver returns\n`unknown`. The function `is_sat_opt` takes a list of propositions and checks for\nthe satisfiability of their conjunction, returning `none` in the case of\n`unknown`; it also takes an argument for an optional timeout.\n\nThe function `get_model` takes a list of propositions and an optional timeout\nand returns a model for the conjunction of those propositions if the SMT solver\nfinds one in time; it returns `none` otherwise. Variables in a model can be\ninspected using `query_model`, which will return\n`none` if a variable is not present in the model or if it is of a type that\ncannot be concretely represented in Formulog (for example, Formulog does not\nhave a concrete representation of a 13-bit vector).\n"
  },
  {
    "path": "docs/lang_ref/program_safety.md",
    "content": "---\ntitle: Program Safety\nlayout: page\nparent: Language Reference\nnav_order: 2\n---\n\n# Program Safety\n\nFormulog imposes some restrictions on programs so that it can give guarantees\nabout runtime behavior. Beyond the type system, these restrictions fall into\ntwo categories: restrictions on variable usage and restrictions on negation.\n\n## Variable Usage\n\nCorrect variable usage is a tricky aspect of logic programming; this section\ndescribes Formulog's restrictions on this front.\n\n### Anonymous Variables\n\nTo help catch bugs related to variable usage, Formulog requires that every\nvariable that does not start with an underscore occurs more than once in its\nscope. Every variable that begins with an underscore is treated as \"anonymous,\"\nin that every occurrence of that variable represents a distinct variable. For\nthis reason, Formulog does not allow any variable that begins with an underscore\nto occur more than once in a scope, except for the traditional anonymous\nvariable `_`.\n\n### Binding Variables\n\nFormulog requires that every variable in a rule is \"bound.\" In what follows, we\nuse the identifiers `p` for a relation, `c` for a constructor, and `f` for a\nfunction. A variable is bound when:\n\n* it is explicitly unified via the `=` built-in predicate with a term that does\n  not contain any unbound variables (e.g., `X` and `Y` are bound via the atom\n  `(X, 0) = (42, Y)`);\n* it is an anonymous variable that occurs as a top-level argument to a negated\n  atom (e.g., `_X` is considered bound in `!p(_X)`); or\n* it occurs in the argument to a positive atom in the rule body, and that\n  occurrence is not also the argument to a function call. For example, the\n  occurrence of `X` in the atom `p(c(X))` is bound, but it is not bound in the\n  atom `p(f(X))`.\n  \nFurthemore, the ML fragment of Formulog is evaluated using call-by-value\nsemantics, which means that the arguments to a function need to be normalized\nand ground (i.e., variable-free) before a call site can be evaluated.\nConsequently, every variable occurring as an argument to a function must be\nbound going into the call site.\n\nTo check whether these variable binding conditions are met, the Formulog runtime\ncurrently performs a single pass over the rule, going from left to right across\nthe rule body, and then finishing on the rule head. This means that the order in\nwhich variables are bound affects whether the Formulog runtime accepts a rule or\nnot, even though different orders might be logically equivalent. For example,\nthis rule is disallowed, because the call to `f(X)` occurs syntactically before\nthe binding of `X` (given a left-to-right reading of the rule):\n\n```\nnot_ok :- p(f(X)), p(X).\n```\n\nThis rule would be accepted if it were rewritten as:\n\n```\nok :- p(X), p(f(X)).\n```\n\nSimilarly, the checker is overly conservative in its treatment of the `=`\npredicate, rejecting rules that require \"sub-unifications\" to occur in an order\nthat is not left to right:\n\n```\nnot_ok :- (_, X) = (f(X), 42).\n```\n\nHere, the second argument of the tuple needs to be unified before the first\nargument can be. An equivalent rule would be accepted:\n\n```\nok :- X = 42, _ = f(X).\n```\n\nIn theory, Formulog could rewrite rules in situations like these to make it\neasier to meet the binding requirements. It currently does not do so because\ntype checking is flow-sensitive, and so the runtime would need to ensure that\nany rewriting results in a rule that is still well-typed. Although we have not\nfound our current approach to be a hindrance in practice, this is something that\nwe could implement in the future.\n\n## Negation, Aggregation, and Stratification\n\nTo ensure that every program has a least model, Formulog requires the use of\nstratified negation and aggregation, a common restriction in Datalog.\nIntuitively, this restriction ensures that there are no recursive\nnegation/aggregation dependencies between predicates.\n\nSince Formulog allows predicate symbols to appear in function bodies,\ndetermining whether a Formulog program is stratified is slightly more\ncomplicated than in the Datalog case. To determine if a program is stratified\nwe construct a dependence graph, where each node represents a different\npredicate. There is a \"positive\" edge from a predicate `p` to a predicate `q`\nif `p` appears in a positive atom in the body of a rule defining `q`. There is\na \"negative\" edge from `p` to `q` if `p` appears in a negated atom in the body\nof a rule defining `q`, or `p` appears in the body of an expression that may be\ntransitively invoked from a rule defining `q`. A program is stratified if there\nare no cycles in the dependence graph that include a \"negative\" edge.\n"
  },
  {
    "path": "docs/pubs.md",
    "content": "---\ntitle: Publications and Artifacts\nlayout: page\nnav_order: 6\n---\n\n# Publications and Artifacts\n\nCheck out our academic papers to learn more about the science behind Formulog.\n\n## Formulog Language Design \n\nOur [OOPSLA'20 paper](https://dl.acm.org/doi/10.1145/3428209) introduces Formulog, motivates its language design, and demonstrates its use through three extensive case studies.\n\nTo cite this paper, please use an entry like this one:\n\n```\n@article{Bembenek2020Formulog,\n  author  = {Aaron Bembenek and Michael Greenberg and Stephen Chong},\n  title   = {Formulog: {D}atalog for {SMT}-{B}ased {S}tatic {A}nalysis},\n  journal = {Proceedings of the {ACM} on Programming Languages},\n  year    = {2020},\n  volume  = {4},\n  number  = {OOPSLA},\n  doi     = {10.1145/3428209},\n  pages   = {141:1--141:31}\n}\n```\n\nThe refereed artifact for this paper is [available on Zenodo](https://zenodo.org/records/4039122). \n\n## Formulog Performance \n\nOur [OOPSLA'24 paper](https://dl.acm.org/doi/10.1145/3689754) describes speeding up Formulog via 1) compilation to Soufflé and 2) eager evaluation, a novel Datalog evaluation strategy that works well for some SMT-heavy workloads.\n\nTo cite this paper, please use an entry like this one:\n\n```\n@article{Bembenek2024Making,\n  author  = {Aaron Bembenek and Michael Greenberg and Stephen Chong},\n  title   = {Making {F}ormulog {F}ast: An {A}rgument for {U}nconventional {D}atalog {E}valuation},\n  journal = {Proceedings of the {ACM} on Programming Languages},\n  year    = {2024},\n  volume  = {4},\n  number  = {OOPSLA2},\n  doi     = {10.1145/3689754},\n  pages   = {314:1--314:30}\n}\n```\n\nThe refereed artifact for this paper won a [distinguished artifact](https://2024.splashcon.org/track/splash-2024-oopsla-artifacts#distinguished-artifacts) award; it is [available on Zenodo](https://zenodo.org/records/13372573).\n\n## Short Papers\n\nFor more information on how Formulog interfaces with external SMT solvers, see the ICLP'20 extended abstract [Datalog-Based Systems Can Use Incremental SMT Solving](https://arxiv.org/html/2009.09158v1/#EPTCS325.7) by Aaron Bembenek, Michael Ballantyne, Michael Greenberg, and Nada Amin ([PDF available here](https://aaronbembenek.github.io/papers/datalog-incr-smt-iclp2020.pdf)).\n\nFor an introduction to Formulog geared towards an audience already well versed in Datalog, see the Datalog 2.0'22 short paper [Formulog: Datalog + SMT + FP](https://ceur-ws.org/Vol-3203/short2.pdf) by Aaron Bembenek, Michael Greenberg, and Stephen Chong."
  },
  {
    "path": "docs/starting.md",
    "content": "---\ntitle: Getting Started\nlayout: page\nnav_order: 2\n---\n\n# Getting Started\n\nThank you for your interest in Formulog!\nThis page describes how to set up Formulog and provides some pointers on writing Formulog programs.\n\n## Setting up Formulog\n\nThere are three main ways to set up Formulog (listed in increasing order of number of dependencies):\n\n- Using the Docker image\n- Downloading the JAR\n- Building from source\n\n### Use the Docker image\n\nPrebuilt images are available on [Docker Hub](https://hub.docker.com/r/aaronbembenek/formulog/tags).\nIf you have Docker installed, you can spin up an Ubuntu container with Formulog, our custom version of Soufflé, and some example programs by running this command (replace `X.Y.Z` with the latest version):\n\n```bash\ndocker run -it aaronbembenek/formulog:X.Y.Z # may require sudo\n```\n\nThis should place you in the directory `/root/formulog/`, with a file `formulog.jar` and some example Formulog programs in the `examples/` directory.\n\n### Download the JAR\n\nDependencies:\n\n- JRE 11+\n- Z3 (v4.12.2 is known to work; other recent versions should work too)\n\nYou can find a prepackaged JAR file in the [Releases section](https://github.com/HarvardPL/formulog/releases) of the GitHub repository.\n\n### Build from Source\n\nDependencies:\n\n- JDK 11+\n- Maven (v3.9.5 is known to work)\n- Z3 (v4.12.2 is known to work; other recent versions should work too)\n\nTo build an executable JAR, run the command `mvn package` from the project source directory.\nThis will create an executable JAR with a name like `formulog-X.Y.Z-SNAPSHOT-jar-with-dependencies.jar` in the `target/` directory.\n\nIf `mvn package` fails during testing, it might mean that there is a problem connecting with Z3.\nYou can compile without testing by adding the `-DskipTests` flag.\n\n### Test Your Setup\n\nIf you have set up Formulog, you should now have an executable JAR at your fingertips.\nThe JAR expects a single Formulog file as an argument.\nLet's test it on the following Formulog program (available in the Docker image and the base repository directory as `examples/greeting.flg`):\n\n```\n@edb rel entity(string)\nentity(\"Alice\").\nentity(\"Bob\").\nentity(\"World\").\n\nrel greeting(string)\ngreeting(Y) :-\n  entity(X),\n  some(M) = get_model([`#y[string] #= str_concat(\"Hello, \", X)`], none),\n  some(Y) = query_model(#y[string], M).\n```\n\nRun the following command (where `formulog.jar` is replaced with the name of the executable JAR):\n\n```\njava -jar formulog.jar examples/greeting.flg --dump-idb\n```\n\nYou should get results like these, indicating that three `greeting` facts have been derived:\n\n```\nParsing...\nFinished parsing (0.202s)\nType checking...\nFinished type checking (0.024s)\nRewriting and validating...\nFinished rewriting and validating (0.253s)\nEvaluating...\nFinished evaluating (0.354s)\n\n==================== SELECTED IDB RELATIONS ====================\n\n---------- greeting (3) ----------\ngreeting(\"Hello, Alice\")\ngreeting(\"Hello, Bob\")\ngreeting(\"Hello, World\")\n```\n\n## Writing Formulog Programs\n\nNow that you have Formulog set up, the fun part starts: writing Formulog programs!\n\nCheck out our [tutorial]({{ site.base_url }}{% link tutorial/index.md %}) for a walk-through of how to encode a refinement type system in Formulog.\nAdditional short-ish example programs can be found in the `examples/` directory (in the Docker image or repository base directory).\nFor examples of larger developments, see the case studies we have used in publications:\n\n- [a refinement type checker](https://github.com/aaronbembenek/making-formulog-fast/blob/main/benchmarks/dminor/bench.flg)\n- [a bottom-up points-to analysis for Java](https://github.com/aaronbembenek/making-formulog-fast/blob/main/benchmarks/scuba/bench.flg)\n- [a symbolic executor an LLVM fragment](https://github.com/aaronbembenek/making-formulog-fast/blob/main/benchmarks/symex/bench.flg)\n\nSee the [language reference]({{ site.base_url }}{% link lang_ref/index.md %}) for details about Formulog constructs.\n\nSyntax highlighting is available for Visual Studio Code (follow instructions [here](https://github.com/HarvardPL/formulog-syntax)) and Vim (install [misc/flg.vim](https://github.com/HarvardPL/formulog/blob/master/misc/flg.vim)).\n\nFinally, please raise a [GitHub issue](https://github.com/HarvardPL/formulog/issues/new) if you want to try out Formulog but would like additional information or assistance---we're happy to help! :)\n"
  },
  {
    "path": "docs/tutorial/index.md",
    "content": "---\ntitle: Tutorial\nlayout: page\nnav_order: 3\n---\n\n# Tutorial: Building a Refinement Type Checker\n\nIn this tutorial, we'll implement a type checker for a small (but still interesting) refinement type system in Formulog.\nIn particular, we'll implement the declarative, bidirectional type checking rules for the first system in the article [Refinement Types: A Tutorial](https://arxiv.org/abs/2010.07763v1) by Ranjit Jhala and Niki Vazou [1].\nOur hope is that our tutorial gives a good overview of many Formulog features, and a flavor of what it is like to program a nontrivial analysis in Formulog.\n\n### Intended Audience\n\nThis tutorial is intended for the PL practitioner (e.g., a grad student, academic, or research engineer).\nWe assume you are familiar with SMT solving, ML-like functional languages, and logic programming, and also have some level of comfort with formal programming language notation (like inference rules).\nIt is also probably helpful to have read one of our [Formulog publications]({{ site.base_url }}{% link pubs.md %}), which should (hopefully) give a good sense for the overall design of the language and its motivations.\nIf you are not familiar with refinement type checking or bidirectional type systems, you should probably skim the first few sections of the tutorial by Jhala and Vazou [1] (we'll focus on Sections 3.1 and 3.2).\n\n### Help Improve This Tutorial\n\nWe're eager for feedback on ways to make this tutorial clearer and more useful!\nIf you have a question about the content of this tutorial, a suggestion for improvement, or a bug report, please let us know by raising a [GitHub issue](https://github.com/HarvardPL/formulog/issues/new).\n\n### Attribution\n\nThis tutorial includes figures from the article [Refinement Types: A Tutorial](https://arxiv.org/abs/2010.07763v1) by Ranjit Jhala and Niki Vazou [1], which has been published under a [CC BY 4.0 license](https://creativecommons.org/licenses/by/4.0/).\nWe will refer to this article as \"JV\" for short.\n\n## General Approach\n\nOne of the advantages of writing a program analysis in Datalog is to be able to implement analysis logic at the level of mathematical specification.\nFormulog extends this benefit to analyses where the formal specification involves constructing logical terms (i.e., SMT formulas) and determining their satisfiability/validity (i.e., SMT solving).\nOur typical approach when implementing an analysis in Formulog is thus to try to directly translate the formal specification of an analysis---inference rules and mathematical helper functions---into Horn clauses and functional code.\n\nThis is the approach we will follow in this tutorial: directly translate the formalism of JV as we encounter it, and then go back to patch our implementation as necessary.\nConcretely, we will work our way through JV Sections 3.1 and 3.2.\nFor the full, final code, see [tutorial.flg](https://github.com/HarvardPL/formulog/blob/master/examples/tutorial.flg).\n\n## Definitions\n\nThe first formalism we encounter in JV Section 3.1 is a definition of types and environments (Figure 3.1), which also refers to the definitions of predicates (Figure 2.1).\n\n![Figure 3.1](./images/figure_3_1.png)\n\n![Figure 2.1](./images/figure_2_1.png)\n\nWe can encode these BNF-style definitions (along with the definition of constraints) using Formulog's support for algebraic data types:\n\n```\n(* An algebraic data type with a single variant *)\ntype basic_typ =\n    | b_int\n\n(* A type alias *)\ntype var = string\n\n(* Interpreted operations: for simplicity, we'll support just a few *)\ntype op =\n    | o_add\n    | o_eq\n\ntype pred =\n    | p_var(var)\n    | p_bool(bool)\n    | p_int(i32) (* i32 is the type of a 32-bit signed integer *)\n    | p_interp_op(op, pred, pred)\n    | p_conj(pred, pred)\n    | p_disj(pred, pred)\n    | p_neg(pred)\n    | p_ite(pred, pred, pred)\n\ntype constraint =\n    | c_pred(pred)\n    | c_conj(constraint, constraint)\n    | c_imp(var, basic_type, pred, constraint)\n\ntype typ =\n    | t_refined(basic_typ, var, pred)\n    | t_func(var, typ, typ)\n\ntype kind =\n    | k_base\n    | k_star\n\n(* Tuples and lists are built-ins *)\ntype env = (var * typ) list\n```\n\nWe can then similarly encode expressions, following Figure 3.2.\n\n![Figure 3.2](./images/figure_3_2.png)\n\n```\ntype expr =\n    | e_int(i32)\n    | e_var(var)\n    | e_let(var, expr, expr)\n    | e_lambda(var, expr)\n    | e_app(expr, var)\n    | e_annot(expr, typ)\n```\n\n### Well-formedness\n\nThe first judgments---which define type well-formedness---are given in Figure 3.3.\n\n![Figure 3.3](./images/figure_3_3.png)\n\nTypically, in Formulog, you would encode inference rules like these using Horn clauses, so let's do that here.\n\nFirst, we need to declare a relation for well-formedness:\n\n```\nrel wf(env, typ, kind)\n```\n\nWe can then encode the rules one by one, with reference to this relation.\nLet's start with the simplest rule, Wf-Kind:\n\n```\nwf(G, T, k_star) :- wf(G, T, k_base).\n```\n\nHorn clauses are implications (read right to left), so this rule is saying that types that are well-formed at kind base are also well-formed at kind star.\nIdentifiers beginning with an uppercase letter are logic programming variables, which are implicitly universally quantified across the entire rule.\n\nWf-Fun is not too bad to encode either:\n\n```\nwf(G, t_func(X, S, T), k_star) :-\n    wf(G, S, _Ks),\n    wf((X, S) :: G, T, _Kt).\n```\n\nTwo things to note:\n\n- Identifiers that begin with an underscore are anonymous variables. Formulog will reject rules where a non-anonymous variable only appears once (a common bug).\n- As in ML variants, the infix constructor `::` is the cons of a value and a list; in this case, we use it to extend the environment with a new binding (represented as a tuple).\n\nOnce we get to WF-Base, we notice that we're missing something: the premise requires the well-sortedness of a predicate, a judgment that is not defined by JV, although the authors say that this amounts to standard type checking (with refinements ignored).\nWe'll have to declare a relation for this and encode the rules:\n\n```\nrel pred_wf(env, pred, basic_typ)\n```\n\nFirst, we might try to encode that a boolean value is, well, a boolean; to do so, we need to revise our definition of basic types to also include booleans:\n\n```\n(* Revised definition *)\ntype basic_typ =\n    | b_int\n    | b_bool\n```\n\nWe can then encode the rule for boolean literals:\n\n```\npred_wf(_G, p_bool(_B), b_bool).\n```\n\nThe rules are straightforward for most of the other constructs:\n\n```\npred_wf(_G, p_int(_I), b_int).\n\npred_wf(G, p_interp_op(o_add, P1, P2), b_int) :-\n    pred_wf(G, P1, b_int),\n    pred_wf(G, P2, b_int).\n\npred_wf(G, p_interp_op(o_eq, P1, P2), b_bool) :-\n    pred_wf(G, P1, T),\n    pred_wf(G, P2, T).\n\n(* We can define a Horn clause with two heads, meaning both conclusions hold *)\npred_wf(G, p_conj(P1, P2), b_bool),\npred_wf(G, p_disj(P1, P2), b_bool) :-\n    pred_wf(G, P1, b_bool),\n    pred_wf(G, P2, b_bool).\n\npred_wf(G, p_neg(P), b_bool) :-\n    pred_wf(G, P, b_bool).\n\npred_wf(G, p_ite(P1, P2, P3), T) :-\n    pred_wf(G, P1, b_bool),\n    pred_wf(G, P2, T),\n    pred_wf(G, P3, T).\n```\n\nThat leaves us just handling variables; to do so, we need to define what it means to look up a variable in an environment.\nFormulog's first-order functional programming comes in handy for defining this type of helper function:\n\n```\nfun lookup(x: var, g: env): typ option =\n    match g with\n    | [] => none\n    | (y, t) :: rest => if x = y then some(t) else lookup(x, rest)\n    end\n```\n\nThe `option` type (with its constructors `none` and `some`) is built into Formulog.\nWe can now define the judgment for variables, as well as our final judgment for type well-formedness.\n\n```\npred_wf(G, p_var(V), B) :-\n    lookup(V, G) = some(t_refined(B, _, _)).\n\nwf(G, t_refined(B, V, P), k_base) :-\n    K = (V, t_refined(B, V, p_true)),\n    pred_wf(K :: G, P, b_bool),\n```\n\nNote that in the rule defining a case of `pred_wf` we invoke the `lookup` function we defined previously.\nFormulog allows ML-style functions to be invoked from within Horn clauses.\n\n### Converting Constraints and Predicates to SMT\n\nThe next judgments we encounter in JV are those for entailment and subtyping (Figure 3.4).\n\n![Figure 3.4](./images/figure_3_4.png)\n\nThe rule Ent-Emp requires us to determine if a constraint is valid; we can do this in Formulog by using the built-in function `is_valid`, provided that we convert a term of type `constraint` to a term of type `bool smt` (the type in Formulog representing an SMT proposition).\nThat doesn't sound too bad; we can write a function to do that.\nThe conjunction case is straightforward:\n\n```\nfun constraint2smt(c: constraint): bool smt =\n    match c with\n    | c_conj(c1, c2) =>\n        let s1 = constraint2smt(c1) in\n        let s2 = constraint2smt(c2) in\n        `s1 /\\ s2`\n    (* TODO: other cases *)\n    end\n```\n\nNote that SMT formulas are demarcated by backticks, and `/\\` is the built-in notation for SMT conjunction.\n\nNow let's consider another case in the match statement, corresponding to the constructor `c_pred(pred)`.\nHere, we need to construct a term of type `bool smt` out of a term of type `pred`.\nLet's try to do that in a helper function, `pred2smt`:\n\n```\nfun pred2smt(p: pred): bool smt =\n    match p with\n    (* Putting a term in quotes makes it type at the SMT level *)\n    | p_bool(b) => `b`\n    | p_conj(p1, p2) =>\n        let b1 = pred2smt(p1) in\n        let b2 = pred2smt(p2) in\n        `b1 /\\ b2`\n    | p_disj(p1, p2) =>\n        let b1 = pred2smt(p1) in\n        let b2 = pred2smt(p2) in\n        `b1 \\/ b2`\n    | p_neg(p1) => let b = pred2smt(p1) in `~b`\n    | p_interp_op(o_eq, p1, p2) =>\n        let b1 = pred2smt(p1) in\n        let b2 = pred2smt(p2) in\n        `b1 #= b2`\n    (* TODO: other cases *)\n    end\n```\n\nSo far, so good.\nBut how about when we get to the `p_int` case?\nThe function signature requires us to return a term of type `bool smt`, but that doesn't make any sense in this case.\nIn fact, if we take a closer look at how predicates are defined, we can see that the syntax for predicates allow bool-valued and int-valued terms to be mixed.\nWe could go back, and try to redefine the syntax for predicates in a way that distinguishes between bool-valued and int-valued terms.\nHowever, even if this were possible, doing so would have a few downsides:\n\n- it takes us farther away from the formalism of JV;\n- it would likely lead to duplication, since we would need, e.g., two different constructors for equality, one where the subterms are ints and one where the subterms are bools; and\n- it does not seem like a very flexible approach as our language of predicates becomes more complex.\n\nThere is another alternative, which is to push the bool-vs-int distinction into the SMT level, using the SMT theory of algebraic data types (this follows the encoding approach of the Dminor refinement type system [2]).\nTo do so, we'll define a new algebraic data type, representing a value in a predicate (which will be either an integer or a bool):\n\n```\ntype val =\n    | v_int(int)\n    | v_bool(bool)\n```\n\nThis type will only appear in SMT formulas.\nWe can then redefine `pred2smt` to return a term of type `val smt`---i.e., a `val`-valued SMT term---instead of a term of type `bool smt`:\n\n```\nfun pred2smt(p: pred): val smt =\n    match p with\n    | p_bool(b) => `v_bool(b)`\n    | p_conj(p1, p2) =>\n        let v1 = pred2smt(p1) in\n        let v2 = pred2smt(p2) in\n        `v_bool(#v_bool_1(v1) /\\ #v_bool_1(v2))`\n    | p_disj(p1, p2) =>\n        let v1 = pred2smt(p1) in\n        let v2 = pred2smt(p2) in\n        `v_bool(#v_bool_1(v1) \\/ #v_bool_1(v2))`\n    | p_neg(p1) =>\n        let v1 = pred2smt(p1) in\n        `v_bool(~#v_bool_1(v1))`\n    | p_int(n) => `v_int(int_const(n))`\n    | p_interp_op(o, p1, p2) =>\n        let v1 = pred2smt(p1) in\n        let v2 = pred2smt(p2) in\n        match o with\n        | o_eq => `v_bool(v1 #= v2)`\n        | o_add => `v_int(int_add(#v_int_1(v1), #v_int_1(v2)))`\n        end\n    | p_ite(p1, p2, p3) =>\n        let v1 = pred2smt(p1) in\n        let v2 = pred2smt(p2) in\n        let v3 = pred2smt(p3) in\n        `#if #v_bool_1(v1) then v2 else v3`\n    | p_var(x) => `#{x}[val]`\n    end\n```\n\nThere's a lot going on here.\nLet's look at some simple cases first.\n\n- `` p_bool(b) => `v_bool(b)` ``: We have a term `b` of type `bool`; to turn it into a term of type `val`, we wrap it in the constructor `v_bool`; to turn this into a term of type `val smt`, we quote it with backticks.\n- `` p_int(n) => `v_int(int_const(n))` ``: We have a term `n` of type `i32`; we can turn it into a term of type `int smt` (a mathematical integer in SMT land) by wrapping it with the constructor `int_const`, and then supply this term as an argument to the `v_int` constructor to create a term of type `val smt`. Note that even though the `v_int` constructor is defined to take a term of type `int` and not `int smt`, the two types are conflated when occurring within an SMT formula (i.e., between backticks).\n- `` p_var(x) => `#{x}[val]` ``: we take a predicate-level variable `x` and construct an SMT-level variable named `x` that is typed as `val` within the SMT formula. Technically, the syntax `#{x}[val]` creates a Formulog term of type `val sym` (i.e., a `val`-valued SMT variable), and the backticks then raise the type to `val smt`.\n\nIn the other cases, we see the use of the constructors `#v_int_1` and `#v_bool_1`.\nFor all datatypes (that can be expressed at the SMT level), Formulog creates constructors of this form that can be used within SMT formulas to access the arguments of constructors.\nFor example, `#v_int_1` is defined so that `#v_int_1(v_int(n))` is the int `n`, but `#v_int_1(v_bool(_))` is any int.\nThis approach reflects the SMT-LIB theory of algebraic datatypes.\nEssentially, these constructors allow us to coerce (within an SMT formula) a value of type `val` to a `bool` or `int`.\n\nNow that we have `pred2smt`, we can go back and finish our definition of `constraint2smt`:\n\n```\nfun constraint2smt(c: constraint): bool smt =\n    match c with\n    | c_conj(c1, c2) =>\n        let s1 = constraint2smt(c1) in\n        let s2 = constraint2smt(c2) in\n        `s1 /\\ s2`\n    | c_pred(p) => let s = pred2smt(p) in `#v_bool_1(s)`\n    | c_imp(x, _b, p1, c1) =>\n        (* Note that we do not actually need to use the basic type `_b` *)\n        let prem = pred2smt(p1) in\n        let conl = constraint2smt(c1) in\n        (* This uses more special syntax for SMT formulas *)\n        `forall #{x}[val]. #v_bool_1(prem) ==> conl`\n    end\n```\n\n### Entailment and Subtyping\n\nNow that we have a way to turn constraints into terms of type `bool smt`, we can start implementing the rules for entailment and subtyping (Figure 3.4, repeated from above).\n\n![Figure 3.4](./images/figure_3_4.png)\n\nFirst, the rules for entailment:\n\n```\nrel ent(env, constraint)\n\nent([], C) :- is_valid(constraint2smt(C)).\n\nent((X, t_refined(B, X, P)) :: G, C) :-\n    ent(G, c_imp(X, B, P, C)).\n```\n\nThe function `is_valid` is a built-in that calls out to an external SMT solver.\n\nFor the subtyping rules, we need to define helper functions for substituting variables in predicates (described briefly in Section 2.2) and types (explicitly given in Section 3.3.1).\n\n![Section 3.3.1](./images/section_3_3_1.png)\n\n```\nfun subst_pred(p: pred, y: var, z: var): pred =\n    match p with\n    | p_bool(_) | p_int(_) => p\n    | p_var(x) => p_var(if x = y then z else x)\n    | p_interp_op(o, p1, p2) =>\n        let p1 = subst_pred(p1, y, z) in\n        let p2 = subst_pred(p2, y, z) in\n        p_interp_op(o, p1, p2)\n    | p_disj(p1, p2) =>\n        let p1 = subst_pred(p1, y, z) in\n        let p2 = subst_pred(p2, y, z) in\n        p_disj(p1, p2)\n    | p_conj(p1, p2) =>\n        let p1 = subst_pred(p1, y, z) in\n        let p2 = subst_pred(p2, y, z) in\n        p_conj(p1, p2)\n    | p_neg(p1) => \n        let p1 = subst_pred(p1, y, z) in\n        p_neg(p1)\n    | p_ite(p1, p2, p3) =>\n        let p1 = subst_pred(p1, y, z) in\n        let p2 = subst_pred(p2, y, z) in\n        let p3 = subst_pred(p3, y, z) in\n        p_ite(p1, p2, p3)\n    end\n\nfun subst_typ(t: typ, y: var, z: var): typ =\n    match t with\n    | t_refined(b, v, p) =>\n        if v = y then t else t_refined(b, v, subst_pred(p, y, z)) \n    | t_func(x, s, t) =>\n        let s = subst_typ(s, y, z) in\n        let t = if x = y then t else subst_typ(t, y, z) in\n        t_func(x, s, t)\n    end\n```\n\nThese functions are pretty standard.\nWe can then define the rules for subtyping:\n\n```\nrel sub(env, typ, typ)\n\nsub(G, t_refined(B, V1, P1), t_refined(B, V2, P2)) :-\n    ent(G, c_imp(V1, B, P1, c_pred(subst_pred(P2, V2, V1)))).\n\nsub(G, t_func(X1, S1, T1), t_func(X2, S2, T2)) :-\n    sub(G, S2, S1),\n    sub((X2, S2) :: G, subst_typ(T1, X1, X2), T2).\n```\n\n### Type Synthesis\n\nGiven the machinery we have in place, the rules for type synthesis (Figure 3.5) fall into place nicely.\n\n![Figure 3.5](./images/figure_3_5.png)\n\n```\nrel syn(env, expr, typ)\n\n(* Declare relation for type checking, so we can refer to it *)\nrel chk(env, expr, typ)\n\nsyn(G, e_var(X), T) :- lookup(X, G) = some(T).\n\n(* Here we give an int n the type int{v : v = n} *)\nsyn(_G, e_int(N), t_refined(b_int, \"v\", P)) :-\n    P = p_interp_op(o_eq, p_var(\"v\"), p_int(N)).\n\nsyn(G, e_annot(E, T), T) :-\n    wf(G, T, _k),\n    chk(G, E, T).\n\nsyn(G, e_app(E, Y), subst_typ(T, X, Y)) :-\n    syn(G, E, t_func(X, S, T)),\n    chk(G, e_var(Y), S).\n```\n\n### Type Checking\n\nThe type checking rules themselves (Figure 3.6) are also straightforward.\n\n![Figure 3.6](./images/figure_3_6.png)\n\n```\nchk(G, E, T) :-\n    syn(G, E, S),\n    sub(G, S, T).\n\nchk(G, e_lambda(X, E), t_func(X, T1, T2)) :-\n    chk((X, T1) :: G, E, T2).\n\nchk(G, e_let(X, E1, E2), T2) :-\n    syn(G, E1, T1),\n    chk((X, T1) :: G, E2, T2).\n```\n\n## Running Some Examples\n\n\nNow that we have all the logic in place, let's try to type check some terms!\nFirst, we'll add a little machinery for running examples:\n\n```\n(* We'll populate this relation with numbered examples *)\nrel ex(i32, env, expr, typ)\n\nrel check_ex(i32)\n\ncheck_ex(N) :-\n    ex(N, G, E, T),\n    chk(G, E, T).\n\n(* This is a query that checks all examples; replace the _ with a number to check a particular example *)\n:- check_ex(_).\n```\n\n### Checking Literals\n\nLet's try a simple example first:\n\n```\n(* The type int{v: v = n} *)\nfun lit_typ(n: i32): typ =\n    t_refined(b_int, \"v\", p_interp_op(o_eq, p_var(\"v\"), p_int(n)))\n\n(*\n    Expr\n        42\n    Type\n        int{v: v = 42}\n*)\nex(0, [], e_int(42), lit_typ(42)).\n```\n\nWe can ask Formulog to dump the derived queries:\n\n```\njava -jar formulog.jar tutorial.flg --dump-query\n```\n\nDoing so, we'll see that `check_ex(0)` is derived:\n\n```\n==================== QUERY RESULTS (1) ====================\nquery:check_ex(0)\n```\n\nSo, our type checker works on that example!\n\nBut maybe it accepts every term?\nLet's try an example that should fail:\n\n```\n(*\n    Expr\n        42\n    Type\n        int{v: v = 43}\n*)\nex(1, [], e_int(42), lit_typ(43)).\n```\n\nWhen we run Formulog now, we see that `check_ex(1)` is *not* derived; and so our type checker has successfully rejected that example!\n\n### Checking Let Bindings\n\nHere's an example involving a simple let binding:\n\n```\n(*\n    Expr\n        let z = 42 in z\n    Type\n        int{v: v = 42}\n*)\nex(2, [], E, T) :-\n    E = e_let(\"z\", e_int(42), e_var(\"z\")),\n    T = lit_typ(42).\n```\n\nWhen we run this, we see that the fact `check_ex(2)` is not derived, which is wrong: this example should go through.\nWhat's up?\nWe can debug this example by adding `print` statements to rules to essentially track the progress of the type checker.\nFor example, if the first of these print functions fires, but the second one does not, then we know that the entailment call failed: \n\n```\nsub(G, t_refined(B, V1, P1), t_refined(B, V2, P2)) :-\n    print((\"sub ent 0\", G, V1, P1, V2, P2)),\n    ent(G, c_imp(V1, B, P1, c_pred(subst_pred(P2, V2, V1)))),\n    print((\"sub ent 1\", G, V1, P1, V2, P2)).\n```\n\nPrint is a function that takes any type (here a tuple) and returns `true` (a function can be used in a rule body as an atom if it returns a bool).\nAdmittedly, this is not a great debugging experience; hopefully we can come up with something better.\nNonetheless, with a little effort, we see that type checking fails on the recursive entailment rule (which faithfully implements the rule Ent-Ext in JV):\n\n```\nent((X, t_refined(B, X, P)) :: G, C) :-\n    ent(G, c_imp(X, B, P, C)).\n```\n\nThe issue is that the judgment is too strict: it requires that the name of the variable in the context is the same as the name of the bound variable in the refinement.\nWe can fix this by adding another rule if the names are different; this rule changes the name of the variable in the refinement to match the name of the variable in the context, as long as that name does not already appear in the refinement:\n\n```\nfun appears(x: var, p: pred): bool =\n    match p with\n    | p_var(y) => x = y\n    | p_bool(_) | p_int(_) => false\n    | p_interp_op(_, p1, p2)\n    | p_conj(p1, p2)\n    | p_disj(p1, p2) =>\n        appears(x, p1) || appears(x, p2)\n    | p_neg(p1) => appears(x, p1)\n    | p_ite(p1, p2, p3) =>\n        appears(x, p1) || appears(x, p2) || appears(x, p3)\n    end\n\n(* An additional rule *)\nent((X, t_refined(B, Y, P)) :: G, C) :-\n    X != Y && !appears(X, P),\n    ent(G, c_imp(X, B, subst_pred(P, Y, X), C)).\n```\n\nNow the type checker works on this example!\n(This isn't the most general solution: a better technique would be to create a fresh variable and substitute it for `Y` in `P` and `X` in `C`; however, this is good enough for now.)\n\n### Checking Functions\n\nLet's see if we can type a constant function:\n\n```\n(*\n    Expr\n        \\y. 0 \n    Type\n        y:int{y: true} -> int{v: v = 0} \n*)\nex(5, [], e_lambda(\"y\", e_int(0)), t_func(\"y\", t_refined(b_int, \"y\", p_bool(true)), lit_typ(0))).\n```\n\nIt works!\nHow about if we bind that function, but do not actually use it?\n(Note that lambdas need to be annotated with a type using the `e_annot` constructor, as there are no rules for synthesizing a type for a lambda.)\n\n```\n(*\n    Expr\n        let z = \\y. 0 in\n        let x = 42 in\n        x\n    Type\n        int{v: v = 42}\n*)\nex(6, [], E, T) :-\n    Lam_type = t_func(\"y\", t_refined(b_int, \"y\", p_bool(true)), lit_typ(0)),\n    E1 = e_let(\"x\", e_int(42), e_var(\"x\")),\n    E = e_let(\"z\", e_annot(e_lambda(\"y\", e_int(0)), Lam_type), E1),\n    T = lit_typ(42).\n```\n\nThis fails!\nWhat gives?\nIf we do some more debugging, we see that entailment is once again an issue: there is no rule for what to do if there is a variable in the context with a function type (in this case, the program variable \"z\").\nWe can fix that by adding another entailment rule that skips over the binding, as long as the bound variable does not appear free in the constraint:\n\n```\nfun is_free(x: var, c: constraint): bool =\n    match c with\n    | c_pred(p) => appears(x, p)\n    | c_conj(c1, c2) => is_free(x, c1) || is_free(x, c2)\n    | c_imp(y, _, p, c1) =>\n        x != y && (appears(x, p) || is_free(x, c1))\n    end\n\n(* An additional rule *)\nent((X, t_func(_, _, _)) :: G, C) :-\n    !is_free(X, C),\n    ent(G, C).\n```\n\nNow the example works :)\n\nHow about if we apply the function?\n\n```\n(*\n    Expr\n        let z = \\y. 0 in\n        let x = 42 in\n        z x\n    Type\n        int{v: v = 0}\n*)\nex(7, [], E, T) :-\n    Lam_type = t_func(\"y\", t_refined(b_int, \"y\", p_bool(true)), lit_typ(0)),\n    E1 = e_let(\"x\", e_int(42), e_app(e_var(\"z\"), \"x\")),\n    E = e_let(\"z\", e_annot(e_lambda(\"y\", e_int(0)), Lam_type), E1),\n    T = lit_typ(0).\n```\n\nIt works!\n\n### Checking Addition\n\nSo far, our examples have not included very interesting refinements.\nLet's try to type check an expression involving addition.\nSince our language of expressions does not actually include operations like addition, we will include an \"add\" function in the context with the appropriate type.\n\n```\n(* The type of the add function *)\nconst add_typ: typ =\n    let add = p_interp_op(o_add, p_var(\"x\"), p_var(\"y\")) in\n    let eq = p_interp_op(o_eq, p_var(\"v\"), add) in\n    let r = t_refined(b_int, \"v\", eq) in\n    let t = t_func(\"y\", t_refined(b_int, \"v\", p_bool(true)), r) in\n    let s = t_refined(b_int, \"v\", p_bool(true)) in\n    t_func(\"x\", s, t)\n\n(*\n    Context\n        add |-> x:int{v: true} -> (y:int{v: true} -> int{v: v = x + y})\n        one |-> int{v: v = 1}\n    Expr\n        let z = 41 in\n        add z one\n    Type\n        int{v: v = 42}\n*)\nex(8, G, E, T) :-\n    G = [(\"add\", add_typ), (\"one\", lit_typ(1))],\n    E = e_let(\"z\", e_int(41), e_app(e_app(e_var(\"add\"), \"z\"), \"one\")),\n    T = lit_typ(42).\n```\n\nIt works!\nWe've been able to prove, in the type system, that our expression evaluates to 42.\n\n## Exercises\n\nAdventurous readers might want to take on these extra exercises.\n\n### Add an Uninterpreted Operation\n\nOur language of predicates includes interpreted operations (like addition and equality), but no uninterpreted operations (the grammar of JV includes both).\nAdd a new `pred` constructor `p_gcd(pred, pred)` that represents the greatest common denominator of two terms.\nWhen a `p_gcd` predicate is converted to an SMT term in the function `pred2smt`, emit an uninterpreted function term.\nSMT-level uninterpreted functions can be declared in Formulog using syntax like:\n\n```\nuninterpreted fun foo(i32 smt, string smt): bool smt\n```\n\nYou'll also have to update rules/functions as appropriate, to handle the new constructor case.\n\n### Add Branches and Recursion\n\nImplement the additional rules for branches and recursion in JV Sections 4.2 and 4.3.\nWe have not tried this ourselves; let us know (by raising a [GitHub issue](https://github.com/HarvardPL/formulog/issues/new)) if you run into difficulties!\n\nFrom skimming the new rules, the trickiest part appears to be creating a fresh variable `y` in the Chk-If rule.\nOne approach you might consider is changing the type of variables from `string` to `val sym` (i.e., a `val`-valued SMT variable):\n\n```\ntype var = val sym\n```\n\nThis will require a bunch of updates in the existing code (a bit of a pain), but it has the advantage that it is now very easy to create a fresh variable.\nFor example, say that you have a context `g` and an expr `e`: the variable `#{(g, e)}[val]` is guaranteed to not occur in either `g` or `e`---i.e., it's fresh.\n(Here, we are using the tuple `(g, e)` as the \"name\" of the variable.)\nWe have found this trick to be useful in implementing more complex type systems. \n\n### Check Out More Complex Formulog Examples\n\nFor our [Formulog publications]({{ site.base_url }}{% link pubs.md %}), we have built three substantial, relatively sophisticated SMT-based case studies.\nAfter going through this tutorial, you might find it interesting to check out the code for these case studies.\nWhile the analyses are more complex than the tutorial example (and, admittedly, not as well documented as they could be), this tutorial will have hopefully armed you with the information to understand a lot of what's happening in them.\n\n- [Dminor](https://github.com/aaronbembenek/making-formulog-fast/blob/main/benchmarks/dminor/bench.flg) [2]: a refinement type checker that allows dynamic type tests, so that types can refer to expressions, and expressions can refer to types.\nType checking also involves proving that expressions are pure, which requires termination checking.\n- [Scuba](https://github.com/aaronbembenek/making-formulog-fast/blob/main/benchmarks/scuba/bench.flg) [3]: a context-sensitive, bottom-up points-to analysis for Java that uses SMT formulas to summarize the effects of methods on the points-to graph.\n- [Symex](https://github.com/aaronbembenek/making-formulog-fast/blob/main/benchmarks/symex/bench.flg): a KLEE-style [4] symbolic executor for a fragment of LLVM bitcode corresponding to simple C programs with loops and arrays.\n\n## Conclusions\n\nIn this tutorial, we've seen how to mechanize the formal specification of an interesting program analysis---the declarative rules for bidirectional refinement type checking---by encoding that specification directly in Formulog.\nIt's neat to be able to program so close to the formal specification; as we've seen, doing so has even allowed us to identify a few possible gaps in the inference rules (i.e., the missing rules for entailment).\n\nFurthermore, now that you have a Formulog implementation of the analysis, you can rely on Formulog's language infrastructure to apply both high-level and low-level optimizations to the analysis.\nFor example, Formulog's parallel evaluation techniques can speed up type checking in the presence of multiple code units.\nAdditionally, the [compiler]({{ site.base_url }}{% link eval_modes/compile.md %}) from Formulog to Soufflé makes it possible to automatically derive a decently efficient C++ version of the type checker.\n\nWe hope you have enjoyed this dive into Formulog!\nAs we mentioned earlier, please raise a [GitHub issue](https://github.com/HarvardPL/formulog/issues/new) for questions, comments, and feedback :)\n\n## References\n\n[1] Ranjit Jhala and Niki Vazou. 2020. Refinement Types: A Tutorial. arXiv:2010.07763v1. <https://arxiv.org/abs/2010.07763v1>\n\n[2] Gavin M. Bierman, Andrew D. Gordon, Cătălin Hriţcu, and David Langworthy. 2012. Semantic Subtyping with an SMT Solver. Journal of Functional Programming 22, 1 (2012), 31–105. <https://doi.org/10.1017/S0956796812000032>\n\n[3] Yu Feng, Xinyu Wang, Isil Dillig, and Thomas Dillig. 2015. Bottom-up Context-Sensitive Pointer Analysis for Java. In Proceedings of the 13th Asian Symposium on Programming Languages and Systems. 465–484. <https://doi.org/10.1007/978-3-319-26529-2_25>\n\n[4] Cristian Cadar, Daniel Dunbar, and Dawson Engler. 2008. KLEE: Unassisted and Automatic Generation of High-Coverage Tests for Complex Systems Programs. In Proceedings of the 8th USENIX Conference on Operating Systems Design and Implementation. 209–224. <https://www.usenix.org/legacy/event/osdi08/tech/full_papers/cadar/cadar.pdf>\n"
  },
  {
    "path": "examples/greeting.flg",
    "content": "(*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2018 - 2019 President and Fellows of Harvard College\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 * #L%\n *)\n\n@edb rel entity(string)\nentity(\"Alice\").\nentity(\"Bob\").\nentity(\"World\").\n\nrel greeting(string)\ngreeting(Y) :-\n  entity(X),\n  some(M) = get_model([`#y[string] #= str_concat(\"Hello, \", X)`], none),\n  some(Y) = query_model(#y[string], M).\n"
  },
  {
    "path": "examples/liquid_types.flg",
    "content": "(*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2018 - 2019 President and Fellows of Harvard College\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 * #L%\n *)\n\n(*******************************************************************************\n  LIQUID_TYPES.FLG\n\n  This file has the implementation of a type checker for a simple language with\n  refinement types.\n*******************************************************************************)\n\ntype tvar = string\n\ntype var = string sym\n\ntype label = string\n\ntype op =\n  | op_not\n  | op_or\n  | op_and\n  | op_eqb\n\ntype base =\n  | base_bool\n  \ntype typ = \n  | typ_tvar(tvar)\n  | typ_fun(var, typ, typ)\n  | typ_forall(tvar, typ)\n  | typ_ref(var, base, exp)\n\nand exp = \n  | exp_var(var)\n  | exp_bool(bool)\n  | exp_op(op)\n  | exp_lam(var, typ, exp)\n  | exp_tlam(tvar, exp)\n  | exp_app(exp, exp)\n  | exp_tapp(exp, typ)\n\nfun append(L1 : 'a list, L2 : 'a list) : 'a list =\n  match L1 with\n  | [] => L2\n  | X :: L1rest => X :: append(L1rest, L2)\n  end\n\nfun remove(X : 'a, L : 'a list) : 'a list =\n  match L with\n  | [] => []\n  | Y :: Lrest  => if X = Y then remove(X, Lrest) else Y :: remove(X, Lrest)\n  end\n\nfun elem(X : 'a, L : 'a list) : bool =\n  match L with\n  | [] => false\n  | Y :: Lrest => X = Y || elem(X, Lrest)\n  end\n\nconst x : var = #x[string]\nconst y : var = #y[string]\nconst b : var = #b[string]\nconst b1 : var = #b1[string]\nconst b2 : var = #b2[string]\nconst v : var = #v[string]\n\nfun fresh_for(NAME : var, VARS : var list) : var =\n  if elem(NAME, VARS)\n  then (* okay, we have work to do *)\n       #{VARS}[string] \n  else (* we're fine---the name is already fresh *)\n       NAME\n\nfun typ_freevars(T : typ) : var list =\n  match T with\n  | typ_tvar(_) => []\n  | typ_fun(X, T1, T2) => append(typ_freevars(T1), remove(X, typ_freevars(T2)))\n  | typ_forall(_, Tinner) => typ_freevars(Tinner)\n  | typ_ref(X, _B, Eref) => remove(X, exp_freevars(Eref))\n  end\n\nand exp_freevars(E : exp) : var list =\n  match E with\n  | exp_var(X) => [X]\n  | exp_bool(_) => []\n  | exp_lam(X, T, Elam) => append(typ_freevars(T), remove(X, exp_freevars(Elam)))\n  | exp_tlam(_, Etlam) => exp_freevars(Etlam)\n  | exp_app(E1, E2) => append(exp_freevars(E1), exp_freevars(E2))\n  | exp_tapp(Etapp, Ttapp) => append(exp_freevars(Etapp), typ_freevars(Ttapp))\n  end\n\n(* SUGAR *)\nfun typ_base(B : base) : typ = typ_ref(v, B, exp_bool(true)).\nfun typ_simple(Tdom : typ, Tcod : typ) : typ = typ_fun(v, Tdom, Tcod)\n\nfun exp_not(E : exp) : exp =\n  exp_app(exp_op(op_not), E).\nfun exp_or(E1 : exp, E2 : exp) : exp =\n  exp_app(exp_app(exp_op(op_or), E1), E2).\nfun exp_and(E1 : exp, E2 : exp) : exp =\n  exp_app(exp_app(exp_op(op_and), E1), E2).\nfun exp_eqb(T : typ, E1 : exp, E2 : exp) : exp =\n  exp_app(exp_app(exp_tapp(exp_op(op_eqb), T), E1), E2)\n\nfun typ_isref(T : typ) : bool =\n  match T with\n  | typ_ref(_, _, _) => true\n  | _ => false\n  end\n\nfun typ_op(O : op) : typ =\n  match O with\n  | op_not => typ_fun(b, typ_base(base_bool),\n                           typ_base(base_bool))\n  | op_or => typ_fun(b1, typ_base(base_bool),\n             typ_fun(b2, typ_base(base_bool),\n                           typ_base(base_bool)))\n  | op_and => typ_fun(b1, typ_base(base_bool),\n              typ_fun(b2, typ_base(base_bool),\n                            typ_base(base_bool)))\n  | op_eqb => typ_forall(\"A\",\n              typ_fun(x, typ_base(base_bool),\n              typ_fun(y, typ_base(base_bool),\n                           typ_ref(v,base_bool,\n                                   exp_eqb(typ_tvar(\"A\"), \n                                           exp_var(v),\n                                           exp_app(exp_app(exp_op(op_eqb),\n                                                           exp_var(x)),\n                                                   exp_var(y)))))))\n  end                                        \n\n\nfun typ_subst(X : var, E : exp, T : typ) : typ =\n  match T with\n  | typ_tvar(_) => T\n  | typ_fun(Y, T1, T2) =>\n    let Yfresh = fresh_for(Y, X::append(exp_freevars(E), typ_freevars(T2))) in\n    let T2fresh = if Y = Yfresh then T2 else typ_subst(Y, exp_var(Yfresh), T2) in\n    typ_fun(Yfresh, \n            typ_subst(X, E, T1),\n            typ_subst(X, E, T2fresh))\n  | typ_forall(A, Tinner) =>\n    typ_forall(A, typ_subst(X, E, Tinner))\n  | typ_ref(Y, B, Eref) =>\n    let Yfresh = fresh_for(Y, X::append(exp_freevars(E), exp_freevars(Eref))) in\n    let Ereffresh = if Y = Yfresh then Eref else exp_subst(Y, exp_var(Yfresh), Eref) in\n    typ_ref(Yfresh,\n            B,\n            exp_subst(X, E, Ereffresh))\n  end\n\nand exp_subst(X: var, E : exp, Etgt : exp) : exp =\n  match Etgt with\n  | exp_var(Y) => if X = Y then E else Etgt\n  | exp_bool(_) => Etgt\n  | exp_op(_) => Etgt\n  | exp_lam(Y, Tlam, Elam) =>\n    let Yfresh = fresh_for(Y, X::append(typ_freevars(Tlam), exp_freevars(Elam))) in\n    let Elamfresh = if Y = Yfresh then Elam else exp_subst(Y, exp_var(Yfresh), Elam) in\n    exp_lam(Yfresh,\n            typ_subst(X, E, Tlam),\n            Elamfresh)\n  | exp_tlam(A, Etlam) =>\n    exp_tlam(A, exp_subst(X, E, Etlam))\n  | exp_app(E1, E2) => \n    exp_app(exp_subst(X, E, E1),\n            exp_subst(X, E, E2))\n  | exp_tapp(Etapp, T) => \n    exp_tapp(exp_subst(X, E, Etapp), typ_subst(X, E, T))\n  end\n\nfun typ_tsubst(A : tvar, T : typ, Ttgt : typ) : typ =\n  match T with\n  | typ_tvar(B) => if A = B then T else Ttgt\n  | typ_fun(X, T1, T2) => \n    typ_fun(X, typ_tsubst(A, T, T1), typ_tsubst(A, T, T2))\n  | typ_forall(B, Tinner) =>\n    let Tnew = if A = B then Tinner else typ_tsubst(A, T, Tinner) in\n    typ_forall(B, Tnew)\n  | typ_ref(X, B, Eref) =>\n    typ_ref(X, B, exp_tsubst(A, T, Eref))\n  end\n\nand exp_tsubst(A : tvar, T : typ, E : exp) : exp =\n  match E with\n  | exp_var(_) | exp_bool(_) => E\n  | exp_op(_) => E\n  | exp_lam(X, Tlam, Elam) =>\n    exp_lam(X, typ_tsubst(A, T, Tlam), exp_tsubst(A, T, Elam))\n  | exp_tlam(B, Etlam) =>\n    let Enew = if A = B then Etlam else exp_tsubst(A, T, Etlam) in\n    exp_tlam(B, Enew)\n  | exp_app(E1, E2) =>\n    exp_app(exp_tsubst(A, T, E1), exp_tsubst(A, T, E2))\n  | exp_tapp(Etapp, Ttapp) =>\n    exp_tapp(exp_tsubst(A, T, Etapp), typ_tsubst(A, T, Ttapp))\n  end\n\ntype ctx  = \n  | ctx_empty\n  | ctx_var(ctx, var, typ)\n  | ctx_tvar(ctx, tvar)\n  | ctx_exp(ctx, exp)\n\nfun lookup(X : var, G : ctx) : typ option =\n  match G with\n  | ctx_empty => none\n  | ctx_var(Grest, Y, T) => if X = Y then some(T) else lookup(X, Grest)\n  | ctx_tvar(Grest, _) => lookup(X, Grest)\n  | ctx_exp(Grest, _) => lookup(X, Grest)\n  end\n\nfun bound_tvar(A : tvar, G : ctx) : bool =\n  match G with\n  | ctx_empty => false\n  | ctx_var(Grest, _, _) => bound_tvar(A, Grest)\n  | ctx_tvar(Grest, B) => A = B || bound_tvar(A, Grest)\n  | ctx_exp(Grest, _) => bound_tvar(A, Grest)\n  end\n\n@topdown\nrel encode_ctx(ctx, bool smt)\n@topdown\nrel encode_exp(exp, bool smt)\n\n@topdown\nrel sub(ctx, typ, typ)\n@topdown\nrel wf_ctx(ctx)\n@topdown\nrel wf_typ(ctx, typ)\n@topdown\nrel wf_exp(ctx, exp, typ)\n@topdown\nrel synth(ctx, exp, typ)\n@topdown\nrel check(ctx, exp, typ)\n\n(*******************************************************************************)\n(* SMT ENCODING ****************************************************************)\n\nencode_ctx(ctx_empty, `true`).\n\nencode_ctx(ctx_tvar(Grest, _), Phi) :-\n  encode_ctx(Grest, Phi).\n\nencode_ctx(ctx_exp(Grest, E), `Phirest /\\ PhiE`) :-\n  encode_ctx(Grest, Phirest),\n  encode_exp(E, PhiE).\n\nencode_ctx(ctx_var(Grest, X, typ_ref(Y, _B, E)), `Phirest /\\ PhiE`) :-\n  encode_ctx(Grest, Phirest),\n  Eprime = exp_subst(Y, exp_var(X), E),\n  encode_exp(Eprime, PhiE).\n\nencode_ctx(ctx_var(Grest, _, T), Phirest) :-\n  encode_ctx(Grest, Phirest),\n  !typ_isref(T).\n\nencode_exp(exp_var(X), V) :-\n  V = `#{X}[bool]`. \n  (* We would need a more sophisticated encoding if there were other types *)\n\nencode_exp(exp_bool(true), `true`).\n\nencode_exp(exp_bool(false), `false`).\n\nencode_exp(exp_app(exp_op(op_not), E), `~Phi`) :-\n  encode_exp(E, Phi).\n\nencode_exp(exp_app(exp_app(exp_op(op_and), E1), E2), `Phi1 /\\ Phi2`) :-\n  encode_exp(E1, Phi1),\n  encode_exp(E2, Phi2).\n\nencode_exp(exp_app(exp_app(exp_op(op_or), E1), E2), `Phi1 \\/ Phi2`) :-\n  encode_exp(E1, Phi1),\n  encode_exp(E2, Phi2).\n\nencode_exp(exp_app(exp_app(exp_tapp(exp_op(op_eqb), _T), E1), E2),\n           `Phi1 #= Phi2`) :-\n  encode_exp(E1, Phi1),\n  encode_exp(E2, Phi2).\n\n(*******************************************************************************)\n(* SUBTYPING *******************************************************************)\n\nsub(G, typ_ref(X, B, E1), typ_ref(Y, B, E2)) :-\n  wf_ctx(G),\n  exp_subst(Y, exp_var(X), E2) = E2prime,\n  encode_ctx(G, PhiG),\n  encode_exp(E1, Phi1),\n  encode_exp(E2prime, Phi2),\n  is_valid(`PhiG /\\ Phi1 ==> Phi2`).\n\nsub(G, typ_tvar(A), typ_tvar(A)) :-\n  wf_ctx(G).\n\nsub(G, typ_fun(X, T11, T12), typ_fun(Y, T21, T22)) :-\n  sub(G, T21, T11),\n  typ_subst(Y, exp_var(X), T22) = T22prime,\n  sub(ctx_var(G, X, T21), T12, T22prime).\n\nsub(G, typ_forall(A, T1), typ_forall(B, T2)) :-\n  typ_tsubst(B, typ_tvar(A), T2) = T2prime,\n  sub(ctx_tvar(G, A), T1, T2prime). \n\n(*******************************************************************************)\n(* WELL FORMEDNESS OF CONTEXTS *************************************************)\n\nwf_ctx(ctx_empty).\nwf_ctx(ctx_var(G, _X, T)) :-\n  wf_ctx(G),\n  wf_typ(G, T).\nwf_ctx(ctx_tvar(G, _)) :-\n  wf_ctx(G).\n\n(*******************************************************************************)\n(* WELL FORMEDNESS OF TYPES ****************************************************)\n\nwf_typ(G, typ_tvar(A)) :-\n  wf_ctx(G),\n  bound_tvar(A, G).\n\nwf_typ(G, typ_fun(X, T1, T2)) :-\n  wf_typ(G, T1),\n  wf_typ(ctx_var(G, X, T1), T2).\n\nwf_typ(G, typ_forall(A, T)) :-\n  wf_typ(ctx_tvar(G, A), T).\n\nwf_typ(G, typ_ref(_X, _B, exp_bool(true))) :-\n  wf_ctx(G).\n\nwf_typ(G, typ_ref(X, B, E)) :-\n  E != exp_bool(true),\n  wf_typ(G, typ_base(B)),\n  check(ctx_var(G,X,typ_base(B)), E, typ_base(base_bool)).\n\n(*******************************************************************************)\n(* WELL FORMEDNESS OF TERMS (i.e., TYPING) *************************************)\n\ncheck(G, E, T) :-\n  synth(G, E, Tprime),\n  sub(G, Tprime, T).\n\nsynth(G, exp_var(X), T) :-\n  wf_ctx(G), \n  lookup(X, G) = some(T),\n  !typ_isref(T).\n\nsynth(G, exp_var(X), typ_ref(Vfresh, B, exp_eqb(T, exp_var(Vfresh), exp_var(V)))) :-\n  wf_ctx(G),\n  lookup(X, G) = some(T),\n  T = typ_ref(V, B, _E),\n  fresh_for(v, typ_freevars(T)) = Vfresh.\n\nsynth(G, exp_bool(true), typ_ref(v, base_bool, exp_var(v))) :-\n  wf_ctx(G).\n\nsynth(G, exp_bool(false), typ_ref(v, base_bool, exp_app(exp_op(op_not), exp_var(v)))) :-\n  wf_ctx(G).\n\nsynth(G, exp_op(O), typ_op(O)) :-\n  wf_ctx(G).\n  \nsynth(G, exp_lam(X, T1, E), T) :-\n  wf_typ(G, T1),\n  synth(ctx_var(G, X, T1), E, T2),\n  typ_fun(X, T1, T2) = T.\n\nsynth(G, exp_app(E1, E2), T) :-\n  synth(G, E1, typ_fun(X, T1, T2)),\n  check(G, E2, T1),\n  typ_subst(X, E2, T2) = T.\n\nsynth(G, exp_tapp(E, Targ), T) :-\n  synth(G, E, typ_forall(A, Tinner)),\n  typ_tsubst(A, Targ, Tinner) = T.\n\n(* Try uncommenting these different queries (one query allowed at a time); you\nmight want to run Formulog with the `--dump-query` option. *)\n\n(*\n:- check(ctx_empty, exp_bool(true), typ_ref(v, base_bool, exp_var(v))).\n*)\n\n(*\n:- check(ctx_empty, exp_bool(true), typ_base(base_bool)).\n*)\n\n(*\n:- wf_typ(ctx_empty, typ_base(base_bool)).\n*)\n\n(*\n:- check(ctx_var(ctx_empty, x, typ_base(base_bool)), \n         exp_var(x),\n         typ_base(base_bool)).\n*)\n\n(*\n:- wf_typ(ctx_empty, typ_ref(x, base_bool, exp_var(x))).\n*)\n\n:- wf_typ(ctx_empty, typ_ref(x, base_bool, exp_app(exp_op(op_not), exp_var(x)))).\n\n"
  },
  {
    "path": "examples/symeval.flg",
    "content": "(*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2018 - 2019 President and Fellows of Harvard College\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 * #L%\n *)\n\n(*******************************************************************************\n  SYMEVAL.FLG\n\n  This file has the implementation of a bounded symbolic evaluator for a simple\n  imperative language.\n*******************************************************************************)\n\ntype ('k, 'v) map = ('k * 'v) list\n\nfun get(K:'k, Map:('k, 'v) map) : 'v option =\n  match Map with\n  | [] => none\n  | (K2, V) :: Rest => if K = K2 then some(V) else get(K, Rest)\n  end\n\nfun put(K:'k, V:'v, Map:('k, 'v) map) : ('k, 'v) map = (K, V) :: Map\n\ntype node = i32\ntype var = string\n\ntype cond =\n  | cond_eq\n  | cond_ne\n  | cond_lt\n  | cond_le\n  | cond_gt\n  | cond_ge\n\ntype binop =\n  | binop_add\n  | binop_mul\n  | binop_div\n  | binop_rem\n    \ntype unop =\n  | unop_neg\n\ntype inst =\n  | inst_jmp(cond, var, var, node)\n  | inst_goto(node)\n  | inst_binop(var, binop, var, var)\n  | inst_unop(var, unop, var)\n  | inst_fail\n  | inst_assign(var, var)\n\ntype val =\n  | v_conc(i32)\n  | v_symb(bv[32] sym)\n\ntype store = (var, val) map\ntype state = (store * bool smt)\n\n@edb rel fall_thru_succ(node, node)\n@edb rel start(node, store)\n@edb rel stmt(node, inst)\nrel reach(node, state, i32)\nrel failed_assert(node, state)\nrel stepping_to(node, state, i32)\n@edb rel max_steps(i32)\n\nfun get_store(State:state) : store =\n  match State with (Store, _) => Store end\n  \nfun get_constraints(State:state) : bool smt =\n  match State with (_, Constraints) => Constraints end\n  \nfun update_store(Reg:var, Val:val, State:state) : state =\n  let (Store, Constraints) = State in\n  let New_store = put(Reg, Val, Store) in\n  (New_store, Constraints)\n\nfun update_constraints(Constraint:bool smt, State:state) : state =\n  let (Store, Constraints) = State in\n  (Store, `Constraint /\\ Constraints`)\n\nfun flip_cond(Cond:cond) : cond =\n  match Cond with\n  | cond_eq => cond_ne\n  | cond_ne => cond_eq\n  | cond_lt => cond_ge\n  | cond_le => cond_gt\n  | cond_gt => cond_le\n  | cond_ge => cond_lt\n  end\n\nfun handle_unop(Dest:var, Op:unop, Val:var, State:state) : state =\n  let Store = get_store(State) in\n  match get(Val, Store) with\n  | some(v_conc(X)) =>\n    let Res =\n      match Op with\n      | unop_neg => -X\n      end in\n    update_store(Dest, v_conc(Res), State)\n  | some(v_symb(Val)) =>\n    let X = #{State}[bv[32]] in\n    let Neg =\n      match Op with\n      | unop_neg => `bv_neg(Val)`\n      end in\n    let State = update_constraints(`X #= Neg`, State) in\n    update_store(Dest, v_symb(X), State)\n  end\n\nfun coerce_symbolic_val(Val:val) : bv[32] smt =\n  match Val with v_conc(X) | v_symb(X) => `X` end\n\nfun handle_binop(Dest:var, Op:binop, Val1:var, Val2:var, State:state) : state =\n    let Store = get_store(State) in\n    let some(Val1) = get(Val1, Store) in\n    let some(Val2) = get(Val2, Store) in\n    match (Val1, Val2) with\n    | (v_conc(X1), v_conc(X2)) =>\n      let Res =\n        match Op with\n        | binop_add => X1 + X2\n        | binop_mul => X1 * X2\n        | binop_div => X1 / X2\n        | binop_rem => X1 % X2\n        end in\n      update_store(Dest, v_conc(Res), State)\n    | _ =>\n      let X = #{State}[bv[32]] in\n      let Val1 = coerce_symbolic_val(Val1) in\n      let Val2 = coerce_symbolic_val(Val2) in\n      let Res =\n        match Op with\n        | binop_add => `bv_add(Val1, Val2)`\n        | binop_mul => `bv_mul(Val1, Val2)`\n        | binop_div => `bv_sdiv(Val1, Val2)`\n        | binop_rem => `bv_srem(Val1, Val2)`\n        end in\n      let State = update_constraints(`X #= Res`, State) in\n      update_store(Dest, v_symb(X), State)\n    end\n\nfun handle_cond(Cond:cond, Reg1:var, Reg2:var, State:state) : state option =\n  let (Store, Constraints) = State in\n  let some(Val1) = get(Reg1, Store) in\n  let some(Val2) = get(Reg2, Store) in\n  match (Val1, Val2) with\n  | (v_conc(X1), v_conc(X2)) =>\n    let Feasible =\n      match Cond with\n      | cond_eq => X1 = X2\n      | cond_ne => X1 != X2\n      | cond_lt => X1 < X2\n      | cond_le => X1 <= X2\n      | cond_gt => X1 > X2\n      | cond_ge => X1 >= X2\n      end in\n    if Feasible then some(State) else none\n  | _ => \n    let Val1 = coerce_symbolic_val(Val1) in\n    let Val2 = coerce_symbolic_val(Val2) in\n    let Constraint =\n      match Cond with\n      | cond_eq => `Val1 #= Val2`\n      | cond_ne => `~(Val1 #= Val2)`\n      | cond_lt => `bv_slt(Val1, Val2)`\n      | cond_le => `~bv_sgt(Val1, Val2)`\n      | cond_gt => `bv_sgt(Val1, Val2)`\n      | cond_ge => `~bv_slt(Val1, Val2)`\n      end in\n    let New_constraints = `Constraint /\\ Constraints` in\n    if is_sat(New_constraints) then\n      let New_state = (Store, New_constraints) in\n      some(New_state)\n    else none\n  end\n\nstepping_to(Node, (Store, `true`), 0) :-\n  start(Node, Store).\n\nstepping_to(Succ, New_state, D) :-\n  fall_thru_succ(Node, Succ),\n  stmt(Node, inst_assign(Dest, Val)),\n  reach(Node, State, D),\n  some(X) = get(Val, get_store(State)),\n  New_state = update_store(Dest, X, State).\n\nstepping_to(Succ, New_state, D) :-\n  fall_thru_succ(Node, Succ),\n  stmt(Node, inst_unop(Dest, Op, Val)),\n  reach(Node, State, D),\n  New_state = handle_unop(Dest, Op, Val, State).\n\nstepping_to(Succ, New_state, D) :-\n  fall_thru_succ(Node, Succ),\n  stmt(Node, inst_binop(Dest, Op, Val1, Val2)),\n  reach(Node, State, D),\n  New_state = handle_binop(Dest, Op, Val1, Val2, State).\n\nstepping_to(Succ, New_state, D) :-\n  stmt(Node, inst_jmp(Cond, Val1, Val2, Succ)),\n  reach(Node, State, D),\n  some(New_state) = handle_cond(Cond, Val1, Val2, State).\n\nstepping_to(Succ, New_state, D) :-\n  fall_thru_succ(Node, Succ),\n  stmt(Node, inst_jmp(Cond, Val1, Val2, _)),\n  reach(Node, State, D),\n  some(New_state) = handle_cond(flip_cond(Cond), Val1, Val2, State).\n\nstepping_to(Succ, State, D) :-\n  stmt(Node, inst_goto(Succ)),\n  reach(Node, State, D).\n\nmax_steps(10).\n\nreach(Node, State, D + 1) :-\n  stepping_to(Node, State, D),\n  max_steps(X),\n  D < X.\n\nfailed_assert(Node, State) :-\n  stmt(Node, inst_fail),\n  reach(Node, State, _).\n\n(* our query... comment this out to exhaustively explore the program states *)\n:- failed_assert(_N, _S).\n\nfun vc(N:i32) : val = v_conc(N).\nfun vs(X:string) : val = v_symb(#{X}[bv[32]]).\n\n(* example 01 *)\n\n(*\nstart(0, []).\n*)\n\n(* example 02\n\n    x = 21 + 21;\n*)\n\n(*\nstart(0, [(\"21\", vc(21))]).\nstmt(0, inst_binop(\"x\", binop_add, \"21\", \"21\")).\nfall_thru_succ(0, 1).\n*)\n\n(* example 03\n\n    x = mksym();\n    if (x != x) {\n        fail();\n    }\n*)\n\n(*\nstart(0, [(\"x\", vs(\"x\"))]).\nstmt(0, inst_jmp(cond_eq, \"x\", \"x\", 2)).\nstmt(1, inst_fail).\nfall_thru_succ(0, 1).\n*)\n\n(* example 04\n\n    x = mksym();\n    if (x == 0) {\n        x = x + 1; \n    }\n    if (x == 0) {\n        fail();\n    }\n*)\n\n(*\nstart(0, [(\"0\", vc(0)), (\"1\", vc(1)), (\"x\", vs(\"x\"))]).\nstmt(0, inst_jmp(cond_ne, \"x\", \"0\", 2)).\nstmt(1, inst_binop(\"x\", binop_add, \"x\", \"1\")).\nstmt(2, inst_jmp(cond_ne, \"x\", \"0\", 4)).\nstmt(3, inst_fail).\nfall_thru_succ(0, 1).\nfall_thru_succ(1, 2).\nfall_thru_succ(2, 3).\n*)\n\n(* example 05\n\n    x = mksym();\n    y = mksym();\n    if (x < 0) {\n        x = -x;\n    }\n    if (y < 0) {\n        y = -y;\n    }\n    z = x + y;\n    if (z < 0) {\n        fail();\n    } \n*)\n\n(*\nstart(2, [(\"0\", vc(0)), (\"x\", vs(\"x\")), (\"y\", vs(\"y\"))]).\nstmt(2, inst_jmp(cond_ge, \"x\", \"0\", 4)).\nstmt(3, inst_unop(\"x\", unop_neg, \"x\")).\nstmt(4, inst_jmp(cond_ge, \"y\", \"0\", 6)).\nstmt(5, inst_unop(\"y\", unop_neg, \"y\")).\nstmt(6, inst_binop(\"z\", binop_add, \"x\", \"y\")).\nstmt(7, inst_jmp(cond_ge, \"z\", \"0\", 9)).\nstmt(8, inst_fail).\nfall_thru_succ(0, 1).\nfall_thru_succ(1, 2).\nfall_thru_succ(2, 3).\nfall_thru_succ(3, 4).\nfall_thru_succ(4, 5).\nfall_thru_succ(5, 6).\nfall_thru_succ(6, 7).\nfall_thru_succ(7, 8).\n*)\n\n(* example 06\n\n    euclid's gCD algorithm\n    \n    x0 = 0;\n    x1 = ...;\n    x2 = ...;\n    while (x2 != x0) {\n        x3 = x2;\n        x2 = x1 % x2;\n        x1 = x3;\n    }\n*)\n\n(*\nstart(0, [(\"x0\", vc(0)), (\"x2\", vc(1071)), (\"x1\", vc(462))]).\nstmt(0, inst_jmp(cond_eq, \"x2\", \"x0\", 1)).\nstmt(2, inst_assign(\"x3\", \"x2\")).\nstmt(3, inst_binop(\"x2\", binop_rem, \"x1\", \"x2\")).\nstmt(4, inst_assign(\"x1\", \"x3\")).\nstmt(5, inst_goto(0)).\nfall_thru_succ(0, 2).\nfall_thru_succ(2, 3).\nfall_thru_succ(3, 4).\nfall_thru_succ(4, 5).\n*)\n\n(* example 07\n\n    x0 = 42;\n    if (x0 == x0) {\n      fail();\n    } else {\n      x0 = x0;\n      x0 = x0;\n      x0 = x0;\n    }\n*)\n\n(*\nstmt(0, inst_jmp(cond_ne, \"x\", \"x\", 2)).\nstmt(1, inst_fail).\nstmt(2, inst_assign(\"x\", \"x\")).\nstmt(3, inst_assign(\"x\", \"x\")).\nstmt(4, inst_assign(\"x\", \"x\")).\nfall_thru_succ(0, 1).\nfall_thru_succ(2, 3).\nfall_thru_succ(3, 4).\nstart(0, [(\"x\", vc(42))]).\n*)\n\n(* example 08\n\n  x = mksym();\n  while (x == x) {\n    // do nothing    \n  }\n  fail();\n\n*)\n\n(*\nstmt(1, inst_jmp(cond_ne, \"x\", \"x\", 2)).\nfall_thru_succ(0, 1).\nfall_thru_succ(1, 1).\nstmt(2, inst_fail).\nstart(1, [(\"x\", vs(\"x\"))]).\n*)\n\n(* example 09\n\n  goto label2;\n  label1:\n  goto label1;\n  label2:\n  fail();\n\n*)\n\n(*\nstmt(0, inst_goto(2)).\nstmt(1, inst_goto(1)).\nstmt(2, inst_fail).\nstart(0, []).\n*)\n\n(* example 10 \n  \n  x = mksym();\n  y = mksym();\n  goto label;\n  while (x == y) {\n    // do nothing\n  }\n  label:\n  fail();\n\n*)\n\n(*\nstmt(0, inst_goto(2)).\nstmt(1, inst_jmp(cond_ne, \"x\", \"y\", 2)).\nstmt(2, inst_fail).\nfall_thru_succ(1, 1).\nstart(0, [(\"x\", vs(\"x\")), (\"y\", vs(\"y\"))]).\n*)\n\n(* example 11 \n  \n  x = mksym();\n  y = mksym();\n  while (x == y) {\n    // do nothing\n  }\n  fail();\n\n*)\n\n(*\nstmt(0, inst_jmp(cond_ne, \"x\", \"y\", 1)).\nstmt(1, inst_fail).\nfall_thru_succ(0, 0).\nstart(0, [(\"x\", vs(\"x\")), (\"y\", vs(\"y\"))]).\n*)\n\n(* example 12\n\n  goto end;\n  while (true) {};\n  fail();\n\n*)\n\n(*\nstmt(0, inst_goto(2)).\nstmt(1, inst_jmp(cond_ne, \"1\", \"1\", 2)).\nstmt(2, inst_fail).\nfall_thru_succ(1, 1).\nstart(0, [(\"1\", vc(1))]).\n*)\n\n(* example 13 \n\n    x0 = mksymb();\n    if (x0 == x0) {\n      fail();\n    } else {\n      x0 = x0;\n      x0 = x0;\n      x0 = x0;\n    }\n*)\n\n(*\nstmt(0, inst_jmp(cond_ne, \"x\", \"x\", 2)).\nstmt(1, inst_fail).\nstmt(2, inst_assign(\"x\", \"x\")).\nstmt(3, inst_assign(\"x\", \"x\")).\nstmt(4, inst_assign(\"x\", \"x\")).\nfall_thru_succ(0, 1).\nfall_thru_succ(2, 3).\nfall_thru_succ(3, 4).\nstart(0, [(\"x\", vs(\"x\"))]).\n*)\n\n(* example 14 \n\n    x0 = mksymb();\n    if (x0 != x0) {\n      x0 = x0;\n      x0 = x0;\n      x0 = x0;\n    } else {\n      fail();\n    }\n*)\n\n(*\nstmt(0, inst_jmp(cond_eq, \"x\", \"x\", 1)).\nstmt(1, inst_fail).\nstmt(2, inst_assign(\"x\", \"x\")).\nstmt(3, inst_assign(\"x\", \"x\")).\nstmt(4, inst_assign(\"x\", \"x\")).\nfall_thru_succ(0, 2).\nfall_thru_succ(2, 3).\nfall_thru_succ(3, 4).\nstart(0, [(\"x\", vs(\"x\"))]).\n*)\n\n(* example 15 \n  \n  x = mksym();\n  y = mksym();\n  while (x != y) {\n    x = x + x;\n  }\n  fail();\n\n*)\n\n(*\nstmt(0, inst_jmp(cond_eq, \"x\", \"y\", 2)).\nstmt(1, inst_binop(\"x\", binop_add, \"x\", \"x\")).\nstmt(2, inst_fail).\nfall_thru_succ(0, 1).\nfall_thru_succ(1, 0).\nstart(0, [(\"x\", vs(\"x\")), (\"y\", vs(\"y\"))]).\n*)\n\n(* example 15 \n  \n  x = x + x;\n  x = x + x;\n  x = x + x;\n  x = x + x;\n  x = x + x;\n  x = x + x;\n  x = x + x;\n  x = x + x;\n  x = x + x;\n  x = x + x;\n  fail();\n\n*)\n\n(*\nstmt(0, inst_binop(\"x\", binop_add, \"x\", \"x\")).\nstmt(1, inst_binop(\"x\", binop_add, \"x\", \"x\")).\nstmt(2, inst_binop(\"x\", binop_add, \"x\", \"x\")).\nstmt(3, inst_binop(\"x\", binop_add, \"x\", \"x\")).\nstmt(4, inst_binop(\"x\", binop_add, \"x\", \"x\")).\nstmt(5, inst_binop(\"x\", binop_add, \"x\", \"x\")).\nstmt(6, inst_binop(\"x\", binop_add, \"x\", \"x\")).\nstmt(7, inst_binop(\"x\", binop_add, \"x\", \"x\")).\nstmt(8, inst_binop(\"x\", binop_add, \"x\", \"x\")).\nstmt(9, inst_binop(\"x\", binop_add, \"x\", \"x\")).\nstmt(10, inst_binop(\"x\", binop_add, \"x\", \"x\")).\nstmt(11, inst_fail).\nfall_thru_succ(0, 1).\nfall_thru_succ(1, 2).\nfall_thru_succ(2, 3).\nfall_thru_succ(3, 4).\nfall_thru_succ(4, 5).\nfall_thru_succ(5, 6).\nfall_thru_succ(6, 7).\nfall_thru_succ(7, 8).\nfall_thru_succ(8, 9).\nfall_thru_succ(9, 10).\nfall_thru_succ(10, 11).\nstart(0, [(\"x\", vc(1))]).\n*)\n\n(* example 16\n\n  if (x == y) {\n    x = x + x;\n    x = x + x;\n  } else {\n    y = y + y;\n  }\n  fail();\n\n*)\n\n(*\nstmt(0, inst_jmp(cond_ne, \"x\", \"y\", 3)).\nstmt(1, inst_binop(\"x\", binop_add, \"x\", \"x\")).\nstmt(2, inst_binop(\"x\", binop_add, \"x\", \"x\")).\nstmt(3, inst_binop(\"y\", binop_add, \"y\", \"y\")).\nstmt(4, inst_fail).\nfall_thru_succ(0, 1).\nfall_thru_succ(1, 2).\nfall_thru_succ(2, 4).\nfall_thru_succ(3, 4).\nstart(0, [(\"x\", vs(\"x\")), (\"y\", vs(\"y\"))]).\n*)\n\n(* example 17 \n\n    x0 = mksymb();\n    y = 42;\n    if (x0 != y) {\n      x0 = x0;\n      x0 = x0;\n      x0 = x0;\n    } else {\n      fail();\n    }\n*)\n\n(*\nstmt(0, inst_jmp(cond_eq, \"x\", \"y\", 1)).\nstmt(1, inst_fail).\nstmt(2, inst_assign(\"x\", \"x\")).\nstmt(3, inst_assign(\"x\", \"x\")).\nstmt(4, inst_assign(\"x\", \"x\")).\nfall_thru_succ(0, 2).\nfall_thru_succ(2, 3).\nfall_thru_succ(3, 4).\nstart(0, [(\"x\", vs(\"x\")), (\"y\", vc(42))]).\n*)\n\n(* example 18\n\n    x = mksymb();\n    y = mksymb();\n    z = mksymb();\n    0: if (x != 0)\n    1:  x = x; \n    2: if (y != 0)\n    3:  y = y;\n    4: if (z != 0)\n    5:  z = z;\n    6: fail();\n*)\n\nstmt(0, inst_jmp(cond_eq, \"x\", \"0\", 2)).\nstmt(1, inst_assign(\"x\", \"x\")).\nstmt(2, inst_jmp(cond_eq, \"y\", \"0\", 4)).\nstmt(3, inst_assign(\"y\", \"y\")).\nstmt(4, inst_jmp(cond_eq, \"z\", \"0\", 6)).\nstmt(5, inst_assign(\"z\", \"z\")).\nstmt(6, inst_fail).\nfall_thru_succ(0, 1).\nfall_thru_succ(1, 2).\nfall_thru_succ(2, 3).\nfall_thru_succ(3, 4).\nfall_thru_succ(4, 5).\nfall_thru_succ(5, 6).\nstart(0, [(\"x\", vs(\"x\")), (\"y\", vs(\"y\")), (\"z\", vs(\"z\")), (\"0\", vc(0))]).\n"
  },
  {
    "path": "examples/tutorial.flg",
    "content": "(***\n\nThis is the full code listing for the Formulog tutorial, which is available at\n<https://harvardpl.github.io/formulog/tutorial/>.\n\n***)\n\n(*******************************************************************************\nDEFINITIONS\n*******************************************************************************)\n\n(* An algebraic data type with two variants *)\ntype basic_typ =\n    | b_int\n    | b_bool\n\n(* A type alias *)\ntype var = string\n\n(* Interpreted operations: for simplicity, we'll support just a few *)\ntype op =\n    | o_add\n    | o_eq\n\ntype pred =\n    | p_var(var)\n    | p_bool(bool)\n    (* i32 is the type of a 32-bit signed integer *)\n    | p_int(i32)\n    | p_interp_op(op, pred, pred)\n    | p_conj(pred, pred)\n    | p_disj(pred, pred)\n    | p_neg(pred)\n    | p_ite(pred, pred, pred)\n\ntype constraint =\n    | c_pred(pred)\n    | c_conj(constraint, constraint)\n    | c_imp(var, basic_typ, pred, constraint)\n\ntype typ =\n    | t_refined(basic_typ, var, pred)\n    | t_func(var, typ, typ)\n\ntype kind =\n    | k_base\n    | k_star\n\n(* Tuples and lists are built-in types *)\ntype env = (var * typ) list\n\ntype expr =\n    | e_int(i32)\n    | e_var(var)\n    | e_let(var, expr, expr)\n    | e_lambda(var, expr)\n    | e_app(expr, var)\n    | e_annot(expr, typ)\n\n(*******************************************************************************\nWELL-FORMEDNESS\n*******************************************************************************)\n\nrel wf(env, typ, kind)\n\nwf(G, T, k_star) :- wf(G, T, k_base).\n\nwf(G, t_func(X, S, T), k_star) :-\n    wf(G, S, _Ks),\n    wf((X, S) :: G, T, _Kt).\n\nrel pred_wf(env, pred, basic_typ)\n\npred_wf(_G, p_bool(_B), b_bool).\n\npred_wf(_G, p_int(_I), b_int).\n\npred_wf(G, p_interp_op(o_add, P1, P2), b_int) :-\n    pred_wf(G, P1, b_int),\n    pred_wf(G, P2, b_int).\n\npred_wf(G, p_interp_op(o_eq, P1, P2), b_bool) :-\n    pred_wf(G, P1, T),\n    pred_wf(G, P2, T).\n\n(* We can define a Horn clause with two heads, meaning both conclusions hold *)\npred_wf(G, p_conj(P1, P2), b_bool),\npred_wf(G, p_disj(P1, P2), b_bool) :-\n    pred_wf(G, P1, b_bool),\n    pred_wf(G, P2, b_bool).\n\npred_wf(G, p_neg(P), b_bool) :-\n    pred_wf(G, P, b_bool).\n\npred_wf(G, p_ite(P1, P2, P3), T) :-\n    pred_wf(G, P1, b_bool),\n    pred_wf(G, P2, T),\n    pred_wf(G, P3, T).\n\nfun lookup(x: var, g: env): typ option =\n    match g with\n    | [] => none\n    | (y, t) :: rest => if x = y then some(t) else lookup(x, rest)\n    end\n\npred_wf(G, p_var(V), B) :-\n    lookup(V, G) = some(t_refined(B, _, _)).\n\nwf(G, t_refined(B, V, P), k_base) :-\n    K = (V, t_refined(B, V, p_bool(true))),\n    pred_wf(K :: G, P, b_bool).\n\n(*******************************************************************************\nCONVERTING CONSTRAINTS AND PREDICATES TO SMT\n*******************************************************************************)\n\ntype val =\n    | v_int(int)\n    | v_bool(bool)\n\nfun pred2smt(p: pred): val smt =\n    match p with\n    | p_bool(b) => `v_bool(b)`\n    | p_conj(p1, p2) =>\n        let v1 = pred2smt(p1) in\n        let v2 = pred2smt(p2) in\n        `v_bool(#v_bool_1(v1) /\\ #v_bool_1(v2))`\n    | p_disj(p1, p2) =>\n        let v1 = pred2smt(p1) in\n        let v2 = pred2smt(p2) in\n        `v_bool(#v_bool_1(v1) \\/ #v_bool_1(v2))`\n    | p_neg(p1) =>\n        let v1 = pred2smt(p1) in\n        `v_bool(~#v_bool_1(v1))`\n    | p_int(n) => `v_int(int_const(n))`\n    | p_interp_op(o, p1, p2) =>\n        let v1 = pred2smt(p1) in\n        let v2 = pred2smt(p2) in\n        match o with\n        | o_eq => `v_bool(v1 #= v2)`\n        | o_add => `v_int(int_add(#v_int_1(v1), #v_int_1(v2)))`\n        end\n    | p_ite(p1, p2, p3) =>\n        let v1 = pred2smt(p1) in\n        let v2 = pred2smt(p2) in\n        let v3 = pred2smt(p3) in\n        `#if #v_bool_1(v1) then v2 else v3`\n    | p_var(x) => `#{x}[val]`\n    end\n\nfun constraint2smt(c: constraint): bool smt =\n    match c with\n    | c_conj(c1, c2) =>\n        let s1 = constraint2smt(c1) in\n        let s2 = constraint2smt(c2) in\n        `s1 /\\ s2`\n    | c_pred(p) => let s = pred2smt(p) in `#v_bool_1(s)`\n    | c_imp(x, _b, p1, c1) =>\n        (* Note that we do not actually need to use the basic type `_b` *)\n        let prem = pred2smt(p1) in\n        let conl = constraint2smt(c1) in\n        (* This uses more special syntax for SMT formulas *)\n        `forall #{x}[val]. #v_bool_1(prem) ==> conl`\n    end\n\n(*******************************************************************************\nENTAILMENT AND SUBTYPING\n*******************************************************************************)\n\nfun subst_pred(p: pred, y: var, z: var): pred =\n    match p with\n    | p_bool(_) | p_int(_) => p\n    | p_var(x) => p_var(if x = y then z else x)\n    | p_interp_op(o, p1, p2) =>\n        let p1 = subst_pred(p1, y, z) in\n        let p2 = subst_pred(p2, y, z) in\n        p_interp_op(o, p1, p2)\n    | p_disj(p1, p2) =>\n        let p1 = subst_pred(p1, y, z) in\n        let p2 = subst_pred(p2, y, z) in\n        p_disj(p1, p2)\n    | p_conj(p1, p2) =>\n        let p1 = subst_pred(p1, y, z) in\n        let p2 = subst_pred(p2, y, z) in\n        p_conj(p1, p2)\n    | p_neg(p1) => \n        let p1 = subst_pred(p1, y, z) in\n        p_neg(p1)\n    | p_ite(p1, p2, p3) =>\n        let p1 = subst_pred(p1, y, z) in\n        let p2 = subst_pred(p2, y, z) in\n        let p3 = subst_pred(p3, y, z) in\n        p_ite(p1, p2, p3)\n    end\n\nrel ent(env, constraint)\n\nent([], C) :- is_valid(constraint2smt(C)).\n\nfun appears(x: var, p: pred): bool =\n    match p with\n    | p_var(y) => x = y\n    | p_bool(_) | p_int(_) => false\n    | p_interp_op(_, p1, p2)\n    | p_conj(p1, p2)\n    | p_disj(p1, p2) =>\n        appears(x, p1) || appears(x, p2)\n    | p_neg(p1) => appears(x, p1)\n    | p_ite(p1, p2, p3) =>\n        appears(x, p1) || appears(x, p2) || appears(x, p3)\n    end\n\nent((X, t_refined(B, Y, P)) :: G, C) :-\n    X != Y && !appears(X, P),\n    ent(G, c_imp(X, B, subst_pred(P, Y, X), C)).\n\nent((X, t_refined(B, Y, P)) :: G, C) :-\n    X = Y || !appears(X, P),\n    ent(G, c_imp(X, B, subst_pred(P, Y, X), C)).\n\nent((X, t_refined(B, X, P)) :: G, C) :-\n    ent(G, c_imp(X, B, P, C)).\n\nfun is_free(x: var, c: constraint): bool =\n    match c with\n    | c_pred(p) => appears(x, p)\n    | c_conj(c1, c2) => is_free(x, c1) || is_free(x, c2)\n    | c_imp(y, _, p, c1) =>\n        x != y && (appears(x, p) || is_free(x, c1))\n    end\n\nent((X, t_func(_, _, _)) :: G, C) :-\n    !is_free(X, C),\n    ent(G, C).\n\nfun subst_typ(t: typ, y: var, z: var): typ =\n    match t with\n    | t_refined(b, v, p) =>\n        if v = y then t else t_refined(b, v, subst_pred(p, y, z)) \n    | t_func(x, s, t) =>\n        let s = subst_typ(s, y, z) in\n        let t = if x = y then t else subst_typ(t, y, z) in\n        t_func(x, s, t)\n    end\n\nrel sub(env, typ, typ)\n\nsub(G, t_refined(B, V1, P1), t_refined(B, V2, P2)) :-\n    ent(G, c_imp(V1, B, P1, c_pred(subst_pred(P2, V2, V1)))).\n\nsub(G, t_func(X1, S1, T1), t_func(X2, S2, T2)) :-\n    sub(G, S2, S1),\n    sub((X2, S2) :: G, subst_typ(T1, X1, X2), T2).\n\n(*******************************************************************************\nTYPE SYNTHESIS\n*******************************************************************************)\n\nrel syn(env, expr, typ)\n\n(* Declare relation for type checking, so we can refer to it *)\nrel chk(env, expr, typ)\n\nsyn(G, e_var(X), T) :- lookup(X, G) = some(T).\n\n(* Here we give an int n the type int{v : v = n} *)\nsyn(_G, e_int(N), t_refined(b_int, \"v\", P)) :-\n    P = p_interp_op(o_eq, p_var(\"v\"), p_int(N)).\n\nsyn(G, e_annot(E, T), T) :-\n    wf(G, T, _k),\n    chk(G, E, T).\n\nsyn(G, e_app(E, Y), subst_typ(T, X, Y)) :-\n    syn(G, E, t_func(X, S, T)),\n    chk(G, e_var(Y), S).\n\n(*******************************************************************************\nTYPE CHECKING\n*******************************************************************************)\n\nchk(G, E, T) :-\n    syn(G, E, S),\n    sub(G, S, T).\n\nchk(G, e_lambda(X, E), t_func(X, T1, T2)) :-\n    chk((X, T1) :: G, E, T2).\n\nchk(G, e_let(X, E1, E2), T2) :-\n    syn(G, E1, T1),\n    chk((X, T1) :: G, E2, T2).\n\n(*******************************************************************************\nRUNNING SOME EXAMPLES\n*******************************************************************************)\n\nrel ex(i32, env, expr, typ)\nrel check_ex(i32)\n\ncheck_ex(N) :-\n    ex(N, G, E, T),\n    chk(G, E, T).\n\n:- check_ex(_).\n\nfun lit_typ(n: i32): typ =\n    t_refined(b_int, \"v\", p_interp_op(o_eq, p_var(\"v\"), p_int(n)))\n\n(*\n    Expr\n        42\n    Type\n        int{v: v = 42}\n*)\nex(0, [], e_int(42), lit_typ(42)).\n\n(*\n    Expr\n        43\n    Type\n        int{v: v = 43}\n*)\nex(1, [], e_int(42), lit_typ(43)).\n\n(*\n    Expr\n        let z = 42 in z\n    Type\n        int{v: v = 42}\n*)\nex(2, [], E, T) :-\n    E = e_let(\"z\", e_int(42), e_var(\"z\")),\n    T = lit_typ(42).\n\n(*\n    Expr\n        let y = 0 in\n        let x = 42 in x\n    Type\n        int{v: v = 42}\n*)\nex(3, [], E, T) :-\n    E1 = e_let(\"x\", e_int(42), e_var(\"x\")),\n    E = e_let(\"y\", e_int(0), E1),\n    T = lit_typ(42).\n\n(*\n    Expr\n        let x = 0 in\n        let x = 42 in x\n    Type\n        int{v: v = 42}\n*)\nex(4, [], E, T) :-\n    E1 = e_let(\"x\", e_int(42), e_var(\"x\")),\n    E = e_let(\"x\", e_int(0), E1),\n    T = lit_typ(42).\n\n(*\n    Expr\n        \\y. 0 \n    Type\n        y:int{y: true} -> int{v: v = 0} \n*)\nex(5, [], e_lambda(\"y\", e_int(0)), t_func(\"y\", t_refined(b_int, \"y\", p_bool(true)), lit_typ(0))).\n\n(*\n    Expr\n        let z = \\y. 0 in\n        let x = 42 in\n        x\n    Type\n        int{v: v = 42}\n*)\nex(6, [], E, T) :-\n    Lam_type = t_func(\"y\", t_refined(b_int, \"y\", p_bool(true)), lit_typ(0)),\n    E1 = e_let(\"x\", e_int(42), e_var(\"x\")),\n    E = e_let(\"z\", e_annot(e_lambda(\"y\", e_int(0)), Lam_type), E1),\n    T = lit_typ(42).\n\n(*\n    Expr\n        let z = \\y. 0 in\n        let x = 42 in\n        z x\n    Type\n        int{v: v = 0}\n*)\nex(7, [], E, T) :-\n    Lam_type = t_func(\"y\", t_refined(b_int, \"y\", p_bool(true)), lit_typ(0)),\n    E1 = e_let(\"x\", e_int(42), e_app(e_var(\"z\"), \"x\")),\n    E = e_let(\"z\", e_annot(e_lambda(\"y\", e_int(0)), Lam_type), E1),\n    T = lit_typ(0).\n\n(* The type of the add function *)\nconst add_typ: typ =\n    let add = p_interp_op(o_add, p_var(\"x\"), p_var(\"y\")) in\n    let eq = p_interp_op(o_eq, p_var(\"v\"), add) in\n    let r = t_refined(b_int, \"v\", eq) in\n    let t = t_func(\"y\", t_refined(b_int, \"v\", p_bool(true)), r) in\n    let s = t_refined(b_int, \"v\", p_bool(true)) in\n    t_func(\"x\", s, t)\n\n(*\n    Context\n        add |-> x:int{v: true} -> (y:int{v: true} -> int{v: v = x + y})\n        one |-> int{v: v = 1}\n    Expr\n        let z = 41 in\n        add z one\n    Type\n        int{v: v = 42}\n*)\nex(8, G, E, T) :-\n    G = [(\"add\", add_typ), (\"one\", lit_typ(1))],\n    E = e_let(\"z\", e_int(41), e_app(e_app(e_var(\"add\"), \"z\"), \"one\")),\n    T = lit_typ(42)."
  },
  {
    "path": "license-header",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) $YEAR President and Fellows of Harvard College\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 * #L%\n */"
  },
  {
    "path": "misc/flg.vim",
    "content": "\" Vim syntax file\n\" Language: Formulog\n\" Maintainer: Aaron Bembenek\n\" Latest Revision: 9 May 2019\n\nif exists(\"b:current_syntax\")\n    finish\nendif\n\nsyn match number \"\\v<[0-9]+(\\.[0-9]*)*[lLfFdD]?>\"\nsyn match number \"\\v<[0-9]*\\.[0-9]+[lLfFdD]?>\"\nsyn match keywords \":-\"\nsyn match keywords \"\\~\"\nsyn match keywords \"#=\"\nsyn match keywords '::'\nsyn match keywords \"|\"\nsyn match keywords \"&\"\nsyn match keywords \"+\"\nsyn match keywords \"-\"\nsyn match keywords \"*\"\nsyn match keywords \"/\"\nsyn match keywords \"\\\\\"\nsyn match keywords \"!\"\nsyn match keywords \"=\"\nsyn match keywords \"<\"\nsyn match keywords \">\"\nsyn match keywords \"\\.\"\nsyn match keywords \"{\"\nsyn match keywords \"}\"\nsyn match keywords \"<\\[\"\nsyn match keywords \"]>\"\nsyn keyword keywords type rel const match let if then else end fun in with uninterpreted and sort\nsyn keyword todo contained TODO XXX FIXME\nsyn region comment start=\"(\\*\" end=\"\\*)\" fold contains=todo,comment\nsyn keyword typeKeywords i32 i64 fp32 fp64 list bool option cmp string smt bv fp sym int array\nsyn match variable \"\\v<[A-Z][a-zA-Z0-9_]*>\"\nsyn match keywords \"#let\"\nsyn match keywords \"#if\"\nsyn match annotation \"@\\v<[a-zA-Z0-9_]+>\"\nsyn match bool \"true\"\nsyn match bool \"false\"\nsyn region string start=/\\v\"/ skip=/\\v\\\\./ end=/\\v\"/\nsyn region formula start=\"`\" end=\"`\" contains=number,string,keywords,comment,variable,typeKeywords\nlet b:current_syntax = \"flg\"\n\nhi def link number          Constant\nhi def link string          Constant\nhi def link bool            Constant\nhi def link keywords        Statement\nhi def link comment         Comment \nhi def link typeKeywords    Type\nhi def link variable        Identifier \nhi def link todo            Todo \nhi def link annotation      Special\nhi def link formula         Special\n"
  },
  {
    "path": "pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <groupId>edu.harvard.seas.pl</groupId>\n    <artifactId>formulog</artifactId>\n    <version>0.8.0-SNAPSHOT</version>\n    <name>formulog</name>\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n    </properties>\n    <dependencies>\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <scope>test</scope>\n            <version>4.13.2</version>\n        </dependency>\n        <dependency>\n            <groupId>org.jgrapht</groupId>\n            <artifactId>jgrapht-core</artifactId>\n            <version>1.5.3</version>\n        </dependency>\n        <dependency>\n            <groupId>org.antlr</groupId>\n            <artifactId>antlr4</artifactId>\n            <version>4.13.2</version>\n        </dependency>\n        <dependency>\n            <groupId>org.pcollections</groupId>\n            <artifactId>pcollections</artifactId>\n            <version>5.0.0</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.commons</groupId>\n            <artifactId>commons-lang3</artifactId>\n            <version>3.20.0</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.bcel</groupId>\n            <artifactId>bcel</artifactId>\n            <version>6.12.0</version>\n        </dependency>\n        <dependency>\n            <groupId>com.fasterxml.jackson.core</groupId>\n            <artifactId>jackson-databind</artifactId>\n            <version>2.21.3</version>\n        </dependency>\n        <dependency>\n            <groupId>info.picocli</groupId>\n            <artifactId>picocli</artifactId>\n            <version>4.7.7</version>\n        </dependency>\n    </dependencies>\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.antlr</groupId>\n                <artifactId>antlr4-maven-plugin</artifactId>\n                <version>4.13.2</version>\n                <configuration>\n                    <visitor>true</visitor>\n                </configuration>\n                <executions>\n                    <execution>\n                        <id>antlr</id>\n                        <goals>\n                            <goal>antlr4</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n            <plugin>\n                <artifactId>maven-assembly-plugin</artifactId>\n                <configuration>\n                    <archive>\n                        <manifest>\n                            <mainClass>edu.harvard.seas.pl.formulog.Main</mainClass>\n                        </manifest>\n                    </archive>\n                    <descriptorRefs>\n                        <descriptorRef>jar-with-dependencies</descriptorRef>\n                    </descriptorRefs>\n                </configuration>\n                <executions>\n                    <execution>\n                        <id>make-assembly</id>\n                        <phase>package</phase>\n                        <goals>\n                            <goal>single</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n            <plugin>\n                <groupId>org.codehaus.mojo</groupId>\n                <artifactId>license-maven-plugin</artifactId>\n                <version>2.7.1</version>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-compiler-plugin</artifactId>\n                <version>3.15.0</version>\n                <configuration>\n                    <source>11</source>\n                    <target>11</target>\n                </configuration>\n            </plugin>\n            <plugin>\n                <groupId>com.diffplug.spotless</groupId>\n                <artifactId>spotless-maven-plugin</artifactId>\n                <version>3.4.0</version>\n                <configuration>\n                    <java>\n                        <googleJavaFormat>\n                            <version>1.23.0</version>\n                            <style>GOOGLE</style>\n                        </googleJavaFormat>\n                        <licenseHeader>\n                            <file>${project.basedir}/license-header</file>\n                        </licenseHeader>\n                    </java>\n                </configuration>\n                <executions>\n                    <execution>\n                        <goals>\n                            <goal>check</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "src/main/antlr4/edu/harvard/seas/pl/formulog/parsing/generated/Formulog.g4",
    "content": "grammar Formulog;\n\nprog\n:\n\t(\n\t\tmetadata\n\t\t| stmt\n\t)* EOF\n;\n\ntsvFile\n:\n\ttabSeparatedTermLine* EOF\n;\n\ntabSeparatedTermLine\n:\n\t(term (TAB term)*)? NEWLINE\t\n;\n\n// Program metadata ////////////////////////////////////////////////////////////\n\nmetadata\n:\n\ttopLevelFunDefs '.'? # funDecl\n\t| annotation* relType = 'rel' ID maybeAnnotatedTypeList '.'? # relDecl\n\t| 'type' typeDefLHS EQ type '.'? # typeAlias\n\t| 'type' typeDefLHS EQ typeDefRHS\n\t(\n\t\t'and' typeDefLHS EQ typeDefRHS\n\t)* '.'? # typeDecl\n\t| 'uninterpreted' 'fun' constructorType ':' type '.'? # uninterpFunDecl\n\t| 'uninterpreted' 'sort' typeDefLHS '.'? # uninterpSortDecl\n;\n\nmaybeAnnotatedTypeList\n:\n\t'(' (var ':')? type (',' (var ':')? type)* ')'\n\t| // can be empty\n;\n\nfunDefLHS\n:\n\tID args = varTypeList ':' retType = type\n;\n\ntopLevelFunDefs\n:\n\tintro = (FUN | CONST) funDefLHS EQ term\n\t(\n\t\t'and' funDefLHS EQ term\n\t)*\n;\n\nfunDefs\n:\n\tFUN funDefLHS EQ term\n\t(\n\t\t'and' funDefLHS EQ term\n\t)*\n;\n\nconstructorType\n:\n\tID typeList\n;\n\nvar\n:\n\tVAR\n\t| ID\n;\n\nvarTypeList\n:\n\t'(' var ':' type\n\t(\n\t\t',' var ':' type\n\t)* ')'\n\t| // can be empty\n;\n\ntypeList\n:\n\t'(' type\n\t(\n\t\t',' type\n\t)* ')'\n\t| // can be empty\n\n;\n\ntype0\n:\n\t'(' type ')' # parenType\n\t| TYPEVAR # typeVar\n\t| type0 ID parameterList # typeRef\n\t| (\n\t\t'(' type\n\t\t(\n\t\t\t',' type\n\t\t)* ')'\n\t)? ID parameterList # typeRef\n;\n\ntype\n:\n\ttype0\n\t(\n\t\t'*' type0\n\t)* # tupleType\n;\n\nparameterList\n:\n\t'[' parameter\n\t(\n\t\t',' parameter\n\t)* ']'\n\t| // can be empty\n\n;\n\nparameter\n:\n\t'?' # wildCardParam\n\t| type # typeParam\n\t| INT # intParam \n;\n\ntypeDefLHS\n:\n\t(\n\t\tTYPEVAR\n\t\t| '(' TYPEVAR\n\t\t(\n\t\t\t',' TYPEVAR\n\t\t)* ')'\n\t)? ID\n;\n\ntypeDefRHS\n:\n\tadtDef\n\t| recordDef\n;\n\nadtDef\n:\n\t'|'? constructorType\n\t(\n\t\t'|' constructorType\n\t)*\n\t| // can be empty\n\n;\n\nrecordDef\n:\n\t'{' recordEntryDef\n\t(\n\t\t';' recordEntryDef\n\t)* ';'? '}'\n;\n\nrecordEntryDef\n:\n\tID ':' type\n;\n\nannotation\n:\n\t'@' ID\n;\n\n// Program logic ///////////////////////////////////////////////////////////////\n\nstmt\n:\n\tclause # clauseStmt\n\t| fact # factStmt\n\t| query # queryStmt\n;\n\nclause\n:\n\thead = nonEmptyTermList ':-' body = nonEmptyTermList '.'\n;\n\nfact\n:\n\tterm '.'\n;\n\nquery\n:\n\t':-' term '.'\n;\n\npredicate\n:\n\tID termArgs\n;\n\nfunctor\n:\n\tid =\n\t(\n\t\tID\n\t\t| XID\n\t\t| XVAR\n\t) parameterList termArgs # indexedFunctor\n;\n\ntermArgs\n:\n\t(\n\t\t'('\n\t\t(\n\t\t\tterm\n\t\t\t(\n\t\t\t\t',' term\n\t\t\t)*\n\t\t) ')'\n\t)?\n;\n\nterm\n:\n\tHOLE # holeTerm\n\t| 'fold' '[' ID ']' termArgs # foldTerm\n\t| functor # functorTerm\n\t| list # listTerm\n\t| tuple # tupleTerm\n\t| '(' term ')' # parensTerm\n\t| op =\n\t(\n\t\tMINUS\n\t\t| BANG\n\t\t| PLUS\n\t) term # unopTerm\n\t| term op =\n\t(\n\t\tMUL\n\t\t| DIV\n\t\t| REM\n\t) term # binopTerm\n\t| term op =\n\t(\n\t\tPLUS\n\t\t| MINUS\n\t) term # binopTerm\n\t| < assoc = right > term '::' term # consTerm\n\t| term op =\n\t(\n\t\tLT\n\t\t| LTE\n\t\t| GT\n\t\t| GTE\n\t) term # binopTerm\n\t| term op =\n\t(\n\t\tEQ\n\t\t| NEQ\n\t) term # binopTerm\n\t| term op = AMP term # binopTerm\n\t| term op = CARET term # binopTerm\n\t| term op = AMPAMP term # binopTerm\n\t| term op = BARBAR term # binopTerm\n\t| VAR # varTerm\n\t| QSTRING # stringTerm\n\t| val =\n\t(\n\t\tINT\n\t\t| HEX\n\t) # i32Term\n\t| val = (INTL | HEXL) # i64Term\n\t| val = FP64 # doubleTerm\n\t| val = FP32 # floatTerm\n\t| val =\n\t(\n\t\tFP32_NAN\n\t\t| FP32_POS_INFINITY\n\t\t| FP32_NEG_INFINITY\n\t\t| FP64_NAN\n\t\t| FP64_POS_INFINITY\n\t\t| FP64_NEG_INFINITY\n\t) # specialFPTerm\n\t| '{' recordEntries '}' # recordTerm\n\t| '{' term 'with' recordEntries '}' # recordUpdateTerm\n\t| '`' term '`' # formulaTerm\n\t| '#' '{' term '}' '[' parameter ']' # termSymFormula\n\t| NOT term # notFormula\n\t| < assoc = left > term op = FORMULA_EQ term # binopFormula\n\t| < assoc = right > term op = AND term # binopFormula\n\t| < assoc = right > term op = OR term # binopFormula\n\t| < assoc = right > term op = IMP term # binopFormula\n\t| < assoc = right > term op = IFF term # binopFormula\n\t| '#let' term EQ term 'in' term # letFormula\n\t| quantifier =\n\t(\n\t\tFORALL\n\t\t| EXISTS\n\t) variables = nonEmptyTermList\n\t(\n\t\t':' pattern = nonEmptyTermList\n\t)? '.' boundTerm = term # quantifiedFormula\n\t| '#if' term 'then' term 'else' term # iteTerm\n\t| term ISNOT ID # outermostCtor\n\t| 'match' term 'with' '|'? matchClause\n\t(\n\t\t'|' matchClause\n\t)* 'end' # matchExpr\n\t| 'let' lhs = letBind '=' assign = term 'in' body = term # letExpr\n\t| 'let' funDefs 'in' letFunBody = term # letFunExpr\n\t| 'if' guard = term 'then' thenExpr = term 'else' elseExpr = term # ifExpr\n;\n\nrecordEntries\n:\n\trecordEntry\n\t(\n\t\t';' recordEntry\n\t)* ';'?\n;\n\nrecordEntry\n:\n\tID '=' term\n;\n\nletBind\n:\n\t(\n\t\tterm\n\t\t| '(' term ',' term\n\t\t(\n\t\t\t',' term\n\t\t)* ')'\n\t)\n;\n\nnonEmptyTermList\n:\n\tterm\n\t(\n\t\t',' term\n\t)*\n;\n\nlist\n:\n\t'['\n\t(\n\t\tterm\n\t\t(\n\t\t\t',' term\n\t\t)*\n\t)? ']'\n;\n\ntuple\n:\n\t'(' term ',' term\n\t(\n\t\t',' term\n\t)* ')'\n;\n\nmatchClause\n:\n\tpats = patterns '=>' rhs = term\n;\n\npatterns\n:\n\tterm\n\t(\n\t\t'|' term\n\t)*\n;\n\n// Tokens //////////////////////////////////////////////////////////////////////\n\nAND\n:\n\t'/\\\\'\n;\n\nOR\n:\n\t'\\\\/'\n;\n\nIMP\n:\n\t'==>'\n;\n\nIFF\n:\n\t'<==>'\n;\n\nNOT\n:\n\t'~'\n;\n\nFORMULA_EQ\n:\n\t'#='\n;\n\nFP32_NAN\n:\n\t'fp32_nan'\n;\n\nFP32_POS_INFINITY\n:\n\t'fp32_pos_infinity'\n;\n\nFP32_NEG_INFINITY\n:\n\t'fp32_neg_infinity'\n;\n\nFP64_NAN\n:\n\t'fp64_nan'\n;\n\nFP64_POS_INFINITY\n:\n\t'fp64_pos_infinity'\n;\n\nFP64_NEG_INFINITY\n:\n\t'fp64_neg_infinity'\n;\n\nTYPEVAR\n:\n\t'\\'' ID\n;\n\nXVAR\n:\n\t'#' VAR\n;\n\nVAR\n:\n\t[A-Z_] [a-zA-Z0-9_]*\n;\n\nINT\n:\n\t[0-9]+\n;\n\nINTL\n:\n\t[0-9]+ ('l' | 'L')\n;\n\nHEX\n:\n\t'0x' [0-9a-fA-F]+\n;\n\nHEXL\n:\n\t'0x' [0-9a-fA-F]+ ('l' | 'L')\n;\n\nfragment\nFP\n:\n\tINT '.' INT\n;\n\nfragment\nFPE\n:\n\t(\n\t\tFP\n\t\t| INT\n\t)\n\t(\n\t\t'e'\n\t\t| 'E'\n\t) ('+'|'-')? INT\n;\n\nFP32\n:\n\t(\n\t\tFP\n\t\t| INT\n\t\t| FPE\n\t)\n\t(\n\t\t'F'\n\t\t| 'f'\n\t)\n;\n\nFP64\n:\n\t(\n\t\tFP\n\t\t| FPE\n\t)\n\t(\n\t\t'D'\n\t\t| 'd'\n\t)?\n\t| INT\n\t(\n\t\t'D'\n\t\t| 'd'\n\t)\n;\n\nLT\n:\n\t'<'\n;\n\nLTE\n:\n\t'<='\n;\n\nGT\n:\n\t'>'\n;\n\nGTE\n:\n\t'>='\n;\n\nMUL\n:\n\t'*'\n;\n\nDIV\n:\n\t'/'\n;\n\nREM\n:\n\t'%'\n;\n\nPLUS\n:\n\t'+'\n;\n\nMINUS\n:\n\t'-'\n;\n\nBANG\n:\n\t'!'\n;\n\nCARET\n:\n\t'^'\n;\n\nAMP\n:\n\t'&'\n;\n\nBARBAR\n:\n\t'||'\n;\n\nAMPAMP\n:\n\t'&&'\n;\n\nISNOT\n:\n\t'not'\n;\n\nEQ\n:\n\t'='\n;\n\nNEQ\n:\n\t'!='\n;\n\nFORALL\n:\n\t'forall'\n;\n\nEXISTS\n:\n\t'exists'\n;\n\nFUN\n:\n\t'fun'\n;\n\nCONST\n:\n\t'const'\n;\n\nHOLE\n:\n\t'??'\n;\n\nNEWLINE\n:\n\t('\\r\\n' | [\\n\\r]) -> channel(HIDDEN)\n;\n\nTAB\n:\n\t'\\t' -> channel(HIDDEN)\n;\n\nSPACES\n:\n\t[ ]+ -> skip\n;\n\nCOMMENT\n:\n\t'(*'\n\t(\n\t\tCOMMENT\n\t\t| .\n\t)*? '*)' -> skip\n;\n\nXID\n:\n\t'#' ID\n;\n\nID\n:\n\t[a-z] [a-zA-Z0-9_]*\n;\n\nfragment\nESCAPE\n:\n\t'\\\\' .\n;\n\nQSTRING\n:\n\t'\"'\n\t(\n\t\tESCAPE\n\t\t| ~( '\\n' | '\\r' | '\"' | '\\\\' )\n\t)* '\"'\n;"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/Configuration.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog;\n\nimport edu.harvard.seas.pl.formulog.ast.Rule;\nimport edu.harvard.seas.pl.formulog.db.IndexedFactDb;\nimport edu.harvard.seas.pl.formulog.eval.IndexedRule;\nimport edu.harvard.seas.pl.formulog.smt.CheckSatAssumingSolver;\nimport edu.harvard.seas.pl.formulog.smt.PushPopSolver;\nimport edu.harvard.seas.pl.formulog.smt.SmtLibSolver;\nimport edu.harvard.seas.pl.formulog.smt.SmtStatus;\nimport edu.harvard.seas.pl.formulog.smt.SmtStrategy;\nimport edu.harvard.seas.pl.formulog.symbols.FunctionSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.RelationSymbol;\nimport edu.harvard.seas.pl.formulog.util.Dataset;\nimport edu.harvard.seas.pl.formulog.util.EnumerableThreadLocal;\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport edu.harvard.seas.pl.formulog.util.SharedLong;\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport java.io.PrintStream;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.concurrent.atomic.AtomicLong;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\n\npublic final class Configuration {\n\n  private Configuration() {\n    throw new AssertionError(\"impossible\");\n  }\n\n  public static final boolean runTests = propIsSet(\"runTests\");\n  public static final String testFile = System.getProperty(\"testFile\");\n\n  public static final boolean recordFuncDiagnostics = propIsSet(\"timeFuncs\");\n  private static final Map<FunctionSymbol, AtomicLong> funcTimes = new ConcurrentHashMap<>();\n\n  public static final boolean recordRuleDiagnostics = propIsSet(\"timeRules\");\n  private static final Map<Rule<?, ?>, Pair<AtomicLong, AtomicLong>> ruleTimes =\n      new ConcurrentHashMap<>();\n\n  public static final boolean debugSmt = propIsSet(\"debugSmt\");\n  public static final String debugSmtOutDir = getStringProp(\"debugSmtOutDir\", \"solver_logs\");\n\n  public static final boolean timeSmt = propIsSet(\"timeSmt\");\n  public static final boolean smtMemoize = propIsSet(\"smtMemoize\", true);\n  private static final Map<SmtLibSolver, Dataset> perProcessSmtEvalStats =\n      new ConcurrentHashMap<>();\n  private static final Dataset smtEvalStats = new Dataset();\n  private static final AtomicLong smtDeclGlobalsTime = new AtomicLong();\n  private static final AtomicLong smtEncodeTime = new AtomicLong();\n  private static final AtomicLong smtDeclTime = new AtomicLong();\n  private static final AtomicLong smtInferTime = new AtomicLong();\n  private static final AtomicLong smtSerialTime = new AtomicLong();\n  public static final SharedLong smtWaitTime = new SharedLong();\n  private static final AtomicInteger smtNumCallsSat = new AtomicInteger();\n  private static final AtomicInteger smtNumCallsUnsat = new AtomicInteger();\n  private static final AtomicInteger smtNumCallsUnknown = new AtomicInteger();\n  private static final AtomicInteger smtNumCallsDoubleCheck = new AtomicInteger();\n  private static final AtomicInteger smtNumCallsFalseUnknown = new AtomicInteger();\n\n  // XXX I don't think this is being used any more\n  private static final AtomicLong smtTotalTime = new AtomicLong();\n\n  public static void recordSmtTime(long delta) {\n    smtTotalTime.addAndGet(delta);\n  }\n\n  public static long getSmtTotalTime() {\n    return smtTotalTime.get();\n  }\n\n  public static final boolean printRelSizes = propIsSet(\"printRelSizes\");\n  public static final boolean printFinalRules = propIsSet(\"printFinalRules\");\n  public static final boolean simplifyFormulaVars = propIsSet(\"simplifyFormulaVars\", true);\n  public static final boolean debugRounds = propIsSet(\"debugRounds\");\n  public static final boolean debugParallelism = propIsSet(\"debugParallelism\");\n\n  public static final int optimizationSetting = getIntProp(\"optimize\", 0);\n\n  public static final int taskSize = getIntProp(\"taskSize\", 128);\n\n  public static final int smtTaskSize = getIntProp(\"smtTaskSize\", 8);\n  public static final int smtCacheSize = getIntProp(\"smtCacheSize\", 100);\n  public static final String smtSolver;\n\n  static {\n    smtSolver = getStringProp(\"smtSolver\", \"z3\");\n    switch (smtSolver) {\n      case \"z3\":\n      case \"cvc4\":\n      case \"yices\":\n      case \"boolector\":\n        break;\n      default:\n        throw new IllegalArgumentException(\"Unrecognized solver: \" + smtSolver);\n    }\n  }\n\n  public static final String smtLogic = getStringProp(\"smtLogic\", \"ALL\");\n  public static final boolean smtDeclareAdts = propIsSet(\"smtDeclareAdts\", true);\n  public static final boolean smtCacheHardResets = propIsSet(\"smtCacheHardResets\", false);\n  public static final boolean smtUseNegativeLiterals = propIsSet(\"smtUseNegativeLiterals\", false);\n  public static final boolean smtDoubleCheckUnknowns = propIsSet(\"smtDoubleCheckUnknowns\", true);\n  public static final boolean smtUseSingleShotSolver =\n      propIsSet(\"smtUseSingleShotSolver\", false) || smtSolver.equals(\"boolector\");\n  public static final boolean smtCheckSuccess = propIsSet(\"smtCheckSuccess\", false);\n\n  private static final Dataset pushPopStackSize = new Dataset();\n  private static final Dataset pushPopStackReuse = new Dataset();\n  private static final Dataset pushPopStackPushes = new Dataset();\n  private static final Dataset pushPopStackPops = new Dataset();\n  private static final Dataset pushPopStackDelta = new Dataset();\n  private static final Dataset csaCacheHitRate = new Dataset();\n  private static final Dataset csaCacheUseRate = new Dataset();\n  private static final Dataset csaCacheSize = new Dataset();\n  private static final Dataset csaCacheHits = new Dataset();\n  private static final Dataset csaCacheMisses = new Dataset();\n  private static final AtomicInteger csaCacheClears = new AtomicInteger();\n  private static final Dataset csaEvalStats = new Dataset();\n  private static final Dataset pushPopEvalStats = new Dataset();\n  private static final Dataset otherSolverEvalStats = new Dataset();\n\n  public static final boolean oneRuleAtATime = propIsSet(\"oneRuleAtATime\");\n  public static final boolean parallelizeInnerLoops = propIsSet(\"parallelizeInnerLoops\", true);\n\n  public static final boolean useDemandTransformation = propIsSet(\"useDemandTransformation\", true);\n  public static final boolean restoreStratification = propIsSet(\"restoreStratification\", true);\n\n  public static final List<String> trackedRelations = getListProp(\"trackedRelations\");\n\n  public static final boolean debugMst = propIsSet(\"debugMst\");\n  public static final boolean debugStratification = propIsSet(\"debugStratification\");\n  public static final String debugStratificationOutDir =\n      getStringProp(\"debugStratificationOutDir\", \"stratification_graphs\");\n\n  public static final boolean testCodeGen = propIsSet(\"testCodeGen\");\n  public static final boolean testCodeGenEager = propIsSet(\"testCodeGenEager\");\n  public static final boolean keepCodeGenTestDirs = propIsSet(\"keepCodeGenTestDirs\");\n  public static final String cxxCompiler = getStringProp(\"cxxCompiler\", null);\n\n  public static final String souffleInclude = System.getProperty(\"souffleInclude\");\n  public static final String boostInclude = System.getProperty(\"boostInclude\");\n  public static final String boostLib = System.getProperty(\"boostLib\");\n  public static final String tbbInclude = System.getProperty(\"tbbInclude\");\n  public static final String tbbLib = System.getProperty(\"tbbLib\");\n  public static final String outputExec = System.getProperty(\"outputExec\");\n\n  public static final int memoizeThreshold() {\n    return getIntProp(\"memoizeThreshold\", 0);\n  }\n\n  public static final boolean genComparators = propIsSet(\"genComparators\", true);\n  public static final boolean minIndex = propIsSet(\"minIndex\", true);\n\n  public static final boolean inlineInRules = propIsSet(\"inlineInRules\", true);\n\n  public static final boolean eagerSemiNaive = propIsSet(\"eagerSemiNaive\");\n  public static final boolean codegenSplitOnSmt = propIsSet(\"codegenSplitOnSmt\");\n\n  public static final boolean useHashDbFilter = propIsSet(\"useHashDbFilter\");\n\n  public static final boolean recordWork = propIsSet(\"recordWork\");\n  public static final boolean recordDetailedWork = propIsSet(\"recordDetailedWork\");\n  public static final SharedLong work = new SharedLong();\n  public static final SharedLong workItems = new SharedLong();\n  public static final SharedLong newDerivs = new SharedLong();\n  public static final SharedLong dupDerivs = new SharedLong();\n  public static final Map<IndexedRule, SharedLong> workPerRule = new ConcurrentHashMap<>();\n\n  public static class SmtStats {\n    public long time;\n    public long ncalls;\n\n    public void add(long time) {\n      this.time += time;\n      ncalls++;\n    }\n  }\n\n  public static final EnumerableThreadLocal<SmtStats> smtTime =\n      new EnumerableThreadLocal<>(SmtStats::new);\n  public static final SharedLong smtCacheClears = new SharedLong();\n  public static final SharedLong smtCacheHits = new SharedLong();\n  public static final SharedLong smtCacheMisses = new SharedLong();\n\n  static {\n    if (recordFuncDiagnostics) {\n      Runtime.getRuntime()\n          .addShutdownHook(\n              new Thread() {\n\n                @Override\n                public void run() {\n                  printFuncDiagnostics(System.err);\n                }\n              });\n    }\n    if (recordRuleDiagnostics) {\n      Runtime.getRuntime()\n          .addShutdownHook(\n              new Thread() {\n\n                @Override\n                public void run() {\n                  printRuleDiagnostics(System.err);\n                }\n              });\n    }\n    if (timeSmt) {\n      Runtime.getRuntime()\n          .addShutdownHook(\n              new Thread() {\n\n                @Override\n                public void run() {\n                  printSmtDiagnostics(System.err);\n                }\n              });\n    }\n    Runtime.getRuntime()\n        .addShutdownHook(\n            new Thread() {\n\n              @Override\n              public void run() {\n                printConfiguration(System.err);\n              }\n            });\n  }\n\n  public static synchronized void printConfiguration(PrintStream out) {\n    // out.println(\"[CONFIG] noResults=\" + noResults);\n    // out.println(\"[CONFIG] timeRules=\" + recordRuleDiagnostics);\n    // out.println(\"[CONFIG] timeFuncs=\" + recordFuncDiagnostics);\n    // out.println(\"[CONFIG] timeSmt=\" + timeSmt);\n    // out.println(\"[CONFIG] optimize=\" + optimizationSetting);\n    // out.println(\"[CONFIG] taskSize=\" + taskSize);\n    // out.println(\"[CONFIG] smtTaskSize=\" + smtTaskSize);\n    // out.println(\"[CONFIG] memoizeThreshold=\" + memoizeThreshold());\n    // out.println(\"[CONFIG] noModel=\" + noModel());\n  }\n\n  public static void recordSmtDeclTime(long time) {\n    smtDeclTime.addAndGet(time);\n  }\n\n  public static void recordSmtInferTime(long time) {\n    smtInferTime.addAndGet(time);\n  }\n\n  public static void recordSmtSerialTime(long time) {\n    smtSerialTime.addAndGet(time);\n  }\n\n  public static void recordSmtEvalTime(\n      SmtLibSolver solver, long encodeTime, long evalTime, SmtStatus result) {\n    smtEncodeTime.addAndGet(encodeTime);\n    smtEvalStats.addDataPoint(evalTime);\n    if (solver instanceof CheckSatAssumingSolver) {\n      csaEvalStats.addDataPoint(evalTime);\n    } else if (solver instanceof PushPopSolver) {\n      pushPopEvalStats.addDataPoint(evalTime);\n    } else {\n      otherSolverEvalStats.addDataPoint(evalTime);\n    }\n    Util.lookupOrCreate(perProcessSmtEvalStats, solver, () -> new Dataset()).addDataPoint(evalTime);\n    switch (result) {\n      case SATISFIABLE:\n        smtNumCallsSat.incrementAndGet();\n        break;\n      case UNKNOWN:\n        smtNumCallsUnknown.incrementAndGet();\n        break;\n      case UNSATISFIABLE:\n        smtNumCallsUnsat.incrementAndGet();\n        break;\n    }\n  }\n\n  public static void recordSmtWaitTime(long time) {\n    smtWaitTime.add(time);\n  }\n\n  public static void recordSmtDeclGlobalsTime(long time) {\n    smtDeclGlobalsTime.addAndGet(time);\n  }\n\n  public static synchronized void printSmtDiagnostics(PrintStream out) {\n    Dataset callsPerSolver = new Dataset();\n    Dataset timePerSolver = new Dataset();\n    for (Dataset ds : perProcessSmtEvalStats.values()) {\n      callsPerSolver.addDataPoint(ds.size());\n      timePerSolver.addDataPoint(ds.computeSum());\n    }\n    out.println(\"[SMT WAIT TIME] \" + smtWaitTime.unsafeGet() / 1e6 + \"ms\");\n    out.println(\"[SMT DECL GLOBALS TIME] \" + smtDeclGlobalsTime.get() / 1e6 + \"ms\");\n    out.println(\"[SMT ENCODE TIME - TOTAL] \" + smtEncodeTime.get() / 1e6 + \"ms\");\n    out.println(\"[SMT ENCODE TIME - DECL] \" + smtDeclTime.get() / 1e6 + \"ms\");\n    out.println(\"[SMT ENCODE TIME - INFER] \" + smtInferTime.get() / 1e6 + \"ms\");\n    out.println(\"[SMT ENCODE TIME - SERIAL] \" + smtSerialTime.get() / 1e6 + \"ms\");\n    out.printf(\"[SMT EVAL TIME] %1.1fms%n\", smtEvalStats.computeSum() / 1e6);\n    out.println(\"[SMT EVAL TIME PER CALL (ms)] \" + smtEvalStats.getStatsString(1e-6));\n    out.println(\"[SMT EVAL TIME PER SOLVER (ms)] \" + timePerSolver.getStatsString(1e-6));\n    out.println(\"[SMT NUM CALLS PER SOLVER] \" + callsPerSolver.getStatsString());\n    out.println(\"[SMT NUM CALLS - SAT] \" + smtNumCallsSat);\n    out.println(\"[SMT NUM CALLS - UNSAT] \" + smtNumCallsUnsat);\n    out.println(\"[SMT NUM CALLS - UNKNOWN] \" + smtNumCallsUnknown);\n    out.println(\"[SMT NUM CALLS - DOUBLE CHECK] \" + smtNumCallsDoubleCheck);\n    out.println(\"[SMT NUM CALLS - FALSE UNKNOWN] \" + smtNumCallsFalseUnknown);\n    if (csaEvalStats.size() > 0) {\n      out.println(\"--- CSA ---\");\n      out.printf(\"[CSA EVAL TIME] %1.1fms%n\", csaEvalStats.computeSum() / 1e6);\n      out.println(\"[CSA EVAL TIME PER CALL (ms)] \" + csaEvalStats.getStatsString(1e-6));\n      out.println(\"[CSA CACHE LIMIT] \" + smtCacheSize);\n      out.println(\"[CSA CACHE BASE SIZE] \" + csaCacheSize.getStatsString());\n      out.println(\"[CSA CACHE HITS] \" + csaCacheHits.getStatsString());\n      out.println(\"[CSA CACHE MISSES] \" + csaCacheMisses.getStatsString());\n      out.println(\"[CSA CACHE HIT RATE] \" + csaCacheHitRate.getStatsString());\n      out.println(\"[CSA CACHE USE RATE] \" + csaCacheUseRate.getStatsString());\n      out.println(\"[CSA CACHE CLEARS] \" + csaCacheClears.get());\n    }\n    if (pushPopEvalStats.size() > 0) {\n      out.println(\"--- PUSH POP ---\");\n      out.printf(\"[PUSH POP EVAL TIME] %1.1fms%n\", pushPopEvalStats.computeSum() / 1e6);\n      out.println(\"[PUSH POP EVAL TIME PER CALL (ms)] \" + pushPopEvalStats.getStatsString(1e-6));\n      out.println(\"[PUSH POP STACK BASE SIZE] \" + pushPopStackSize.getStatsString());\n      out.println(\"[PUSH POP STACK PUSHES] \" + pushPopStackPushes.getStatsString());\n      out.println(\"[PUSH POP STACK POPS] \" + pushPopStackPops.getStatsString());\n      out.println(\"[PUSH POP STACK DELTA] \" + pushPopStackDelta.getStatsString());\n      out.println(\"[PUSH POP STACK REUSE] \" + pushPopStackReuse.getStatsString());\n    }\n    if (otherSolverEvalStats.size() > 0) {\n      out.println(\"--- OTHER ---\");\n      out.printf(\"[OTHER EVAL TIME] %1.1fms%n\", otherSolverEvalStats.computeSum() / 1e6);\n      out.println(\"[OTHER EVAL TIME PER CALL (ms)] \" + otherSolverEvalStats.getStatsString(1e-6));\n    }\n  }\n\n  public static void recordPushPopSolverStats(\n      int solverId, int stackStartSize, int pops, int pushes) {\n    pushPopStackSize.addDataPoint(stackStartSize);\n    pushPopStackReuse.addDataPoint(stackStartSize - pops);\n    pushPopStackPops.addDataPoint(pops);\n    pushPopStackPushes.addDataPoint(pushes);\n    pushPopStackDelta.addDataPoint(pushes - pops);\n  }\n\n  public static void recordCsaCacheStats(int solverId, int hits, int misses, int oldSize) {\n    int numAsserts = hits + misses;\n    csaCacheHits.addDataPoint(hits);\n    csaCacheMisses.addDataPoint(misses);\n    csaCacheHitRate.addDataPoint(numAsserts == 0 ? 1 : (double) hits / numAsserts);\n    csaCacheUseRate.addDataPoint(oldSize == 0 ? 1 : (double) hits / oldSize);\n    csaCacheSize.addDataPoint(oldSize);\n  }\n\n  public static void recordSmtDoubleCheck(boolean falseUnknown) {\n    smtNumCallsDoubleCheck.incrementAndGet();\n    if (falseUnknown) {\n      smtNumCallsFalseUnknown.incrementAndGet();\n    }\n  }\n\n  public static void recordCsaCacheClear(int solverId) {\n    csaCacheClears.incrementAndGet();\n  }\n\n  public static void recordFuncTime(FunctionSymbol func, long time) {\n    AtomicLong l = Util.lookupOrCreate(funcTimes, func, () -> new AtomicLong());\n    l.addAndGet(time);\n  }\n\n  public static Map<FunctionSymbol, AtomicLong> getFuncDiagnostics() {\n    return Collections.unmodifiableMap(funcTimes);\n  }\n\n  public static synchronized void printFuncDiagnostics(PrintStream out) {\n    Map<FunctionSymbol, AtomicLong> times = Configuration.getFuncDiagnostics();\n    List<Map.Entry<FunctionSymbol, AtomicLong>> sorted =\n        times.entrySet().stream().sorted(sortTimes).collect(Collectors.toList());\n    Iterator<Map.Entry<FunctionSymbol, AtomicLong>> it = sorted.iterator();\n    int end = Math.min(times.size(), 10);\n    for (int i = 0; i < end; ++i) {\n      Map.Entry<FunctionSymbol, AtomicLong> e = it.next();\n      out.println(\"[FUNC DIAGNOSTICS] \" + e.getValue().get() + \"ms: \" + e.getKey());\n    }\n  }\n\n  private static final Comparator<Map.Entry<?, AtomicLong>> sortTimes =\n      new Comparator<Map.Entry<?, AtomicLong>>() {\n\n        @Override\n        public int compare(Entry<?, AtomicLong> e1, Entry<?, AtomicLong> e2) {\n          return -Long.compare(e1.getValue().get(), e2.getValue().get());\n        }\n      };\n\n  private static final Comparator<Map.Entry<?, Pair<AtomicLong, AtomicLong>>> sortPairedTimes =\n      new Comparator<Map.Entry<?, Pair<AtomicLong, AtomicLong>>>() {\n\n        @Override\n        public int compare(\n            Entry<?, Pair<AtomicLong, AtomicLong>> e1, Entry<?, Pair<AtomicLong, AtomicLong>> e2) {\n          return -Long.compare(getTotal(e1), getTotal(e2));\n        }\n\n        private long getTotal(Entry<?, Pair<AtomicLong, AtomicLong>> e) {\n          Pair<AtomicLong, AtomicLong> p = e.getValue();\n          return p.fst().get() + p.snd().get();\n        }\n      };\n\n  public static void recordRulePrefixTime(Rule<?, ?> rule, long time) {\n    Pair<AtomicLong, AtomicLong> p =\n        Util.lookupOrCreate(ruleTimes, rule, () -> new Pair<>(new AtomicLong(), new AtomicLong()));\n    p.fst().addAndGet(time);\n  }\n\n  public static void recordRuleSuffixTime(Rule<?, ?> rule, long time) {\n    Pair<AtomicLong, AtomicLong> p =\n        Util.lookupOrCreate(ruleTimes, rule, () -> new Pair<>(new AtomicLong(), new AtomicLong()));\n    p.snd().addAndGet(time);\n  }\n\n  public static synchronized void printRuleDiagnostics(PrintStream out) {\n    Map<Rule<?, ?>, Pair<AtomicLong, AtomicLong>> times = ruleTimes;\n    List<Map.Entry<Rule<?, ?>, Pair<AtomicLong, AtomicLong>>> sorted =\n        times.entrySet().stream().sorted(sortPairedTimes).collect(Collectors.toList());\n    Iterator<Map.Entry<Rule<?, ?>, Pair<AtomicLong, AtomicLong>>> it = sorted.iterator();\n    int end = Math.min(times.size(), 10);\n    for (int i = 0; i < end; ++i) {\n      Map.Entry<Rule<?, ?>, Pair<AtomicLong, AtomicLong>> e = it.next();\n      Pair<AtomicLong, AtomicLong> p = e.getValue();\n      long pre = p.fst().get();\n      long suf = p.snd().get();\n      long total = pre + suf;\n      out.println(\n          \"[RULE DIAGNOSTICS] \" + total + \" (\" + pre + \" + \" + suf + \") ms:\\n\" + e.getKey());\n    }\n  }\n\n  public static synchronized void printRelSizes(\n      PrintStream out, String header, IndexedFactDb db, boolean printEmpty) {\n    for (RelationSymbol sym : db.getSymbols()) {\n      if (printEmpty || !db.isEmpty(sym)) {\n        out.println(\n            \"[\"\n                + header\n                + \"] \"\n                + sym\n                + \": \"\n                + db.countDistinct(sym)\n                + \" / \"\n                + db.numIndices(sym)\n                + \" / \"\n                + db.countDuplicates(sym));\n      }\n    }\n  }\n\n  private static boolean propIsSet(String prop, boolean defaultValue) {\n    String val = System.getProperty(prop);\n    if (val == null) {\n      return defaultValue;\n    }\n    if (val.equals(\"true\") || val.equals(\"\")) {\n      return true;\n    }\n    if (val.equals(\"false\")) {\n      return false;\n    }\n    throw new IllegalArgumentException(\"Unexpected argument for property \" + prop + \": \" + val);\n  }\n\n  private static boolean propIsSet(String prop) {\n    return propIsSet(prop, false);\n  }\n\n  static int getIntProp(String prop, int def) {\n    String val = System.getProperty(prop);\n    if (val == null) {\n      return def;\n    }\n    try {\n      return Integer.parseInt(val);\n    } catch (NumberFormatException e) {\n      throw new IllegalArgumentException(\"Property \" + prop + \" expects an integer argument\");\n    }\n  }\n\n  private static String getStringProp(String prop, String def) {\n    String val = System.getProperty(prop);\n    if (val == null) {\n      return def;\n    }\n    return val;\n  }\n\n  static List<String> getListProp(String prop) {\n    String val = System.getProperty(prop);\n    if (val == null || val.equals(\"\")) {\n      return Collections.emptyList();\n    }\n    List<String> l = new ArrayList<>();\n    breakIntoCollection(val, l);\n    return Collections.unmodifiableList(l);\n  }\n\n  private static void breakIntoCollection(String s, Collection<String> acc) {\n    int split;\n    while ((split = s.indexOf(',')) != -1) {\n      String sub = s.substring(0, split);\n      acc.add(sub);\n      if (split == s.length()) {\n        return;\n      }\n      s = s.substring(split + 1);\n    }\n    acc.add(s);\n  }\n\n  static SmtStrategy getSmtStrategy() {\n    String val = System.getProperty(\"smtStrategy\");\n    if (val == null) {\n      val = \"queue-1\";\n    }\n\n    switch (val) {\n      case \"naive\":\n        return new SmtStrategy(SmtStrategy.Tag.NAIVE, null);\n      case \"pushPop\":\n        return new SmtStrategy(SmtStrategy.Tag.PUSH_POP, null);\n      case \"pushPopNaive\":\n        return new SmtStrategy(SmtStrategy.Tag.PUSH_POP_NAIVE, null);\n      case \"perThreadNaive\":\n        return new SmtStrategy(SmtStrategy.Tag.PER_THREAD_NAIVE, null);\n      case \"perThreadPushPop\":\n        return new SmtStrategy(SmtStrategy.Tag.PER_THREAD_PUSH_POP, null);\n      case \"perThreadPushPopNaive\":\n        return new SmtStrategy(SmtStrategy.Tag.PER_THREAD_PUSH_POP_NAIVE, null);\n    }\n\n    Pattern p = Pattern.compile(\"queue-(\\\\d+)\");\n    Matcher m = p.matcher(val);\n    if (m.matches()) {\n      int size = Integer.parseInt(m.group(1));\n      return new SmtStrategy(SmtStrategy.Tag.QUEUE, size);\n    }\n    p = Pattern.compile(\"bestMatch-(\\\\d+)\");\n    m = p.matcher(val);\n    if (m.matches()) {\n      int size = Integer.parseInt(m.group(1));\n      return new SmtStrategy(SmtStrategy.Tag.BEST_MATCH, size);\n    }\n    p = Pattern.compile(\"perThreadQueue-(\\\\d+)\");\n    m = p.matcher(val);\n    if (m.matches()) {\n      int size = Integer.parseInt(m.group(1));\n      return new SmtStrategy(SmtStrategy.Tag.PER_THREAD_QUEUE, size);\n    }\n    p = Pattern.compile(\"perThreadBestMatch-(\\\\d+)\");\n    m = p.matcher(val);\n    if (m.matches()) {\n      int size = Integer.parseInt(m.group(1));\n      return new SmtStrategy(SmtStrategy.Tag.PER_THREAD_BEST_MATCH, size);\n    }\n    throw new IllegalArgumentException(\"Unrecognized SMT strategy: \" + val);\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/FormulogTester.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2021-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog;\n\nimport com.fasterxml.jackson.databind.JsonNode;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.eval.Evaluation;\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport edu.harvard.seas.pl.formulog.eval.EvaluationResult;\nimport edu.harvard.seas.pl.formulog.eval.SemiNaiveEvaluation;\nimport edu.harvard.seas.pl.formulog.functions.DummyFunctionDef;\nimport edu.harvard.seas.pl.formulog.functions.FunctionDef;\nimport edu.harvard.seas.pl.formulog.parsing.ParseException;\nimport edu.harvard.seas.pl.formulog.parsing.Parser;\nimport edu.harvard.seas.pl.formulog.symbols.FunctionSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.RelationSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.Symbol;\nimport edu.harvard.seas.pl.formulog.symbols.SymbolManager;\nimport edu.harvard.seas.pl.formulog.types.TypeChecker;\nimport edu.harvard.seas.pl.formulog.types.TypeException;\nimport edu.harvard.seas.pl.formulog.types.WellTypedProgram;\nimport edu.harvard.seas.pl.formulog.util.sexp.SExp;\nimport edu.harvard.seas.pl.formulog.util.sexp.SExpException;\nimport edu.harvard.seas.pl.formulog.util.sexp.SExpParser;\nimport edu.harvard.seas.pl.formulog.validating.InvalidProgramException;\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.FileReader;\nimport java.io.IOException;\nimport java.io.Reader;\nimport java.io.StringReader;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Function;\n\npublic class FormulogTester {\n\n  private boolean initialized;\n  private final List<FormulogTest> tests = new ArrayList<>();\n  private WellTypedProgram prog;\n\n  public synchronized void setup(Reader program, File testFile)\n      throws ParseException, TypeException, IOException {\n    Parser parser = new Parser();\n    prog = new TypeChecker(parser.parse(program)).typeCheck();\n    loadTests(testFile, parser);\n    initialized = true;\n  }\n\n  private void loadTests(File testFile, Parser parser) throws IOException, ParseException {\n    tests.clear();\n    ObjectMapper objectMapper = new ObjectMapper();\n    JsonNode topLevel = objectMapper.readValue(testFile, JsonNode.class);\n    Path rootTestDir = testFile.getParentFile().toPath();\n    for (JsonNode test : topLevel) {\n      tests.add(loadTest(test, rootTestDir, parser));\n    }\n  }\n\n  private FormulogTest loadTest(JsonNode test, Path rootTestDir, Parser parser)\n      throws ParseException, FileNotFoundException {\n    String name = test.get(\"name\").asText();\n    Path dir = rootTestDir.resolve(name);\n    Map<RelationSymbol, Set<Term[]>> m = new HashMap<>();\n    for (RelationSymbol sym : prog.getFactSymbols()) {\n      if (sym.isEdbSymbol()) {\n        FileReader fr = new FileReader(dir.resolve(sym + \".tsv\").toFile());\n        Set<Term[]> s = parser.parseFacts(sym, fr);\n        m.put(sym, s);\n      }\n    }\n    try {\n      Function<EvaluationResult, List<String>> logic = parseBoolLogic(test.get(\"spec\").asText());\n      return new FormulogTest(name, m, logic);\n    } catch (ParseException e) {\n      throw new ParseException(-1, \"Trouble parsing spec for test \" + name + \": \" + e.getMessage());\n    }\n  }\n\n  private Function<EvaluationResult, List<String>> parseBoolLogic(String s) throws ParseException {\n    Reader r = new StringReader(s);\n    SExpParser p = new SExpParser(r);\n    SExp sexp;\n    try {\n      sexp = p.parse();\n    } catch (SExpException e) {\n      throw new ParseException(-1, e.getMessage());\n    }\n    return parseBoolLogic(sexp);\n  }\n\n  private Function<EvaluationResult, List<String>> parseBoolLogic(SExp sexp) throws ParseException {\n    if (sexp.isAtom()) {\n      return parseBoolAtom(sexp.asAtom());\n    } else {\n      return parseBoolList(sexp.asList());\n    }\n  }\n\n  private Function<EvaluationResult, List<String>> parseBoolAtom(String atom)\n      throws ParseException {\n    switch (atom) {\n      case \"true\":\n        return r -> Collections.emptyList();\n      default:\n        throw new ParseException(-1, \"Unrecognized boolean constant: \" + atom);\n    }\n  }\n\n  private Function<EvaluationResult, List<String>> parseBoolList(List<SExp> l)\n      throws ParseException {\n    if (l.isEmpty()) {\n      throw new ParseException(-1, \"Unexpected empty list\");\n    }\n    SExp head = l.get(0);\n    if (head.isList()) {\n      throw new ParseException(-1, \"Expected an operator, but got a list: \" + head);\n    }\n    String cmd = head.asAtom();\n    switch (cmd) {\n      case \"=\":\n        {\n          if (l.size() != 3) {\n            throw new ParseException(-1, \"Wrong number of arguments for = operator: \" + l.size());\n          }\n          SExp sexp1 = l.get(1);\n          SExp sexp2 = l.get(2);\n          Function<EvaluationResult, Integer> f1 = parseIntLogic(sexp1);\n          Function<EvaluationResult, Integer> f2 = parseIntLogic(sexp2);\n          return r -> {\n            int v1 = f1.apply(r);\n            int v2 = f2.apply(r);\n            if (v1 == v2) {\n              return Collections.emptyList();\n            } else {\n              return Collections.singletonList(\n                  \"Failed equality: (= \"\n                      + sexp1\n                      + \" \"\n                      + sexp2\n                      + \") reduces to (= \"\n                      + v1\n                      + \" \"\n                      + v2\n                      + \")\");\n            }\n          };\n        }\n      case \"and\":\n        {\n          List<Function<EvaluationResult, List<String>>> fs = new ArrayList<>();\n          for (Iterator<SExp> it = l.listIterator(1); it.hasNext(); ) {\n            fs.add(parseBoolLogic(it.next()));\n          }\n          return r -> {\n            List<String> errors = new ArrayList<>();\n            for (Function<EvaluationResult, List<String>> f : fs) {\n              errors.addAll(f.apply(r));\n            }\n            return errors;\n          };\n        }\n      default:\n        throw new ParseException(-1, \"Unrecognized boolean operator: \" + cmd);\n    }\n  }\n\n  private Function<EvaluationResult, Integer> parseIntLogic(SExp sexp) throws ParseException {\n    if (sexp.isAtom()) {\n      return parseIntAtom(sexp.asAtom());\n    } else {\n      return parseIntList(sexp.asList());\n    }\n  }\n\n  private Function<EvaluationResult, Integer> parseIntAtom(String s) throws ParseException {\n    try {\n      int n = Integer.parseInt(s);\n      return r -> n;\n    } catch (NumberFormatException e) {\n      throw new ParseException(-1, \"Expected an integer constant but got \" + s);\n    }\n  }\n\n  private Function<EvaluationResult, Integer> parseIntList(List<SExp> l) throws ParseException {\n    if (l.isEmpty()) {\n      throw new ParseException(-1, \"Unexpected empty list\");\n    }\n    SExp head = l.get(0);\n    if (head.isList()) {\n      throw new ParseException(-1, \"Expected an operator, but got a list: \" + head);\n    }\n    String cmd = head.asAtom();\n    switch (cmd) {\n      case \"size\":\n        if (l.size() != 2) {\n          throw new ParseException(-1, \"Wrong number of arguments for size operator: \" + l.size());\n        }\n        SExp sexp1 = l.get(1);\n        if (sexp1.isList()) {\n          throw new ParseException(\n              -1, \"size operator expects a relation symbol, but got a list: \" + sexp1);\n        }\n        String name = sexp1.asAtom();\n        SymbolManager sm = prog.getSymbolManager();\n        if (!sm.hasName(name)) {\n          throw new ParseException(-1, \"unrecognized relation used in size operator: \" + name);\n        }\n        Symbol sym = sm.lookupSymbol(name);\n        if (!(sym instanceof RelationSymbol)) {\n          throw new ParseException(-1, \"non-relation symbol used in size operator: \" + name);\n        }\n        RelationSymbol rel = (RelationSymbol) sym;\n        return r -> r.getCount(rel);\n      default:\n        throw new ParseException(-1, \"Unrecognized integer operator: \" + cmd);\n    }\n  }\n\n  synchronized boolean runTests() throws InvalidProgramException, EvaluationException {\n    if (!initialized) {\n      throw new IllegalStateException(\"Need to set up tests first.\");\n    }\n    // Need to get the EDBs that are hard-coded into the program.\n    Map<RelationSymbol, Set<Term[]>> hardCodedFacts = new HashMap<>();\n    for (RelationSymbol sym : prog.getFactSymbols()) {\n      hardCodedFacts.put(sym, new HashSet<>(prog.getFacts(sym)));\n    }\n    boolean ok = true;\n    for (FormulogTest test : tests) {\n      ok &= runTest(test, hardCodedFacts);\n    }\n    return ok;\n  }\n\n  private void clearCachedState() {\n    // Need to clear memoized function calls\n    prog.getFunctionCallFactory().clearMemoCache();\n    // Need to reset predicate functions\n    for (FunctionSymbol sym : prog.getFunctionSymbols()) {\n      FunctionDef def = prog.getDef(sym);\n      if (def instanceof DummyFunctionDef) {\n        ((DummyFunctionDef) def).setDef(null);\n      }\n    }\n  }\n\n  private boolean runTest(FormulogTest test, Map<RelationSymbol, Set<Term[]>> hardCodedFacts)\n      throws InvalidProgramException, EvaluationException {\n    for (RelationSymbol sym : prog.getFactSymbols()) {\n      if (sym.isEdbSymbol()) {\n        Set<Term[]> s = prog.getFacts(sym);\n        s.clear();\n        s.addAll(hardCodedFacts.get(sym));\n        s.addAll(test.testInputs.get(sym));\n      }\n    }\n    clearCachedState();\n    // This would be better if we could set up the program, and then load\n    // the external facts, so we do not need to do the setup each time.\n    Evaluation e = SemiNaiveEvaluation.setup(prog, 1, false);\n    System.out.print(test.name + \"... \");\n    e.run();\n    List<String> errors = test.testLogic.apply(e.getResult());\n    if (errors.isEmpty()) {\n      System.out.println(\"PASSED\");\n      return true;\n    } else {\n      System.out.println(\"FAILED\");\n      for (String error : errors) {\n        System.out.println(\">>> \" + error);\n      }\n      return false;\n    }\n  }\n\n  private static class FormulogTest {\n    public final String name;\n    public final Map<RelationSymbol, Set<Term[]>> testInputs;\n    public final Function<EvaluationResult, List<String>> testLogic;\n\n    public FormulogTest(\n        String name,\n        Map<RelationSymbol, Set<Term[]>> testInputs,\n        Function<EvaluationResult, List<String>> testLogic) {\n      this.name = name;\n      this.testInputs = testInputs;\n      this.testLogic = testLogic;\n    }\n  }\n\n  public static void main(String[] args) throws Exception {\n    if (args.length != 1) {\n      System.out.println(\"Expected a single Formulog file as an input.\");\n      System.exit(1);\n    }\n    if (Configuration.testFile == null) {\n      System.out.println(\"Must specify a JSON test file.\");\n      System.exit(1);\n    }\n    main(new File(args[0]), new File(Configuration.testFile));\n  }\n\n  public static void main(File file, File json) throws Exception {\n    FormulogTester tester = new FormulogTester();\n    tester.setup(new FileReader(file), json);\n    System.exit(tester.runTests() ? 0 : 1);\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/Main.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog;\n\nimport edu.harvard.seas.pl.formulog.ast.BasicProgram;\nimport edu.harvard.seas.pl.formulog.ast.BasicRule;\nimport edu.harvard.seas.pl.formulog.ast.Program;\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.ast.UserPredicate;\nimport edu.harvard.seas.pl.formulog.codegen.CodeGen;\nimport edu.harvard.seas.pl.formulog.eval.Evaluation;\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport edu.harvard.seas.pl.formulog.eval.EvaluationResult;\nimport edu.harvard.seas.pl.formulog.eval.SemiNaiveEvaluation;\nimport edu.harvard.seas.pl.formulog.parsing.ParseException;\nimport edu.harvard.seas.pl.formulog.parsing.Parser;\nimport edu.harvard.seas.pl.formulog.smt.AbstractSmtLibSolver;\nimport edu.harvard.seas.pl.formulog.smt.SmtStrategy;\nimport edu.harvard.seas.pl.formulog.symbols.RelationSymbol;\nimport edu.harvard.seas.pl.formulog.types.TypeChecker;\nimport edu.harvard.seas.pl.formulog.types.TypeException;\nimport edu.harvard.seas.pl.formulog.types.WellTypedProgram;\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport edu.harvard.seas.pl.formulog.validating.InvalidProgramException;\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.FileReader;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.TimeUnit;\nimport java.util.stream.Collectors;\nimport org.apache.commons.lang3.time.StopWatch;\nimport picocli.CommandLine;\nimport picocli.CommandLine.Command;\nimport picocli.CommandLine.ITypeConverter;\nimport picocli.CommandLine.Model;\nimport picocli.CommandLine.Option;\nimport picocli.CommandLine.ParameterException;\nimport picocli.CommandLine.Parameters;\nimport picocli.CommandLine.Spec;\n\n@Command(\n    name = \"formulog\",\n    mixinStandardHelpOptions = true,\n    version = \"Formulog 0.8.0\",\n    description = \"Runs Formulog.\")\npublic final class Main implements Callable<Integer> {\n\n  @Spec private static Model.CommandSpec spec;\n\n  @Option(\n      names = \"--smt-solver-mode\",\n      description =\n          \"Strategy to use when interacting with external SMT solvers\"\n              + \"('naive', 'push-pop', or 'check-sat-assuming').\",\n      converter = SmtModeConverter.class)\n  public static SmtStrategy smtStrategy = Configuration.getSmtStrategy();\n\n  @Option(\n      names = \"--eager-eval\",\n      description = \"Use eager evaluation (instead of traditional semi-naive Datalog evaluation)\")\n  public static boolean eagerEval = Configuration.eagerSemiNaive;\n\n  @Option(\n      names = {\"-F\", \"--fact-dir\"},\n      description = \"Directory to look for fact .tsv files (default: '.').\")\n  private List<String> factDirs = Configuration.getListProp(\"factDirs\");\n\n  @Option(\n      names = {\"-D\", \"--output-dir\"},\n      description = \"Directory for .tsv output files (default: '.').\")\n  private File outDir = new File(\".\");\n\n  @Option(\n      names = {\"-j\", \"--parallelism\"},\n      description = \"Number of threads to use.\")\n  public static int parallelism = Configuration.getIntProp(\"parallelism\", 4);\n\n  @Option(names = \"--dump-all\", description = \"Print all relations.\")\n  private boolean dumpAll;\n\n  @Option(names = \"--dump-idb\", description = \"Print all IDB relations.\")\n  private boolean dumpIdb;\n\n  @Option(names = \"--dump\", description = \"Print selected relations.\")\n  private Set<String> relationsToPrint = Collections.emptySet();\n\n  @Option(names = \"--dump-query\", description = \"Print query result.\")\n  private boolean dumpQuery;\n\n  @Option(names = \"--dump-sizes\", description = \"Print relation sizes.\")\n  private boolean dumpSizes;\n\n  @Option(\n      names = {\"-c\", \"--codegen\"},\n      description = \"Compile the Formulog program.\")\n  private boolean codegen;\n\n  @Option(\n      names = {\"--codegen-dir\"},\n      description = \"Directory for generated code (default: './codegen').\")\n  private File codegenDir = new File(\"codegen\");\n\n  @Option(\n      names = {\"--smt-stats\"},\n      description = \"Report basic statistics related to SMT solver usage.\")\n  public static boolean smtStats = false;\n\n  @Parameters(index = \"0\", description = \"Formulog program file.\")\n  private File file;\n\n  private final StopWatch clock = new StopWatch();\n  private volatile boolean interrupted = true;\n\n  private static final boolean exnStackTrace = System.getProperty(\"exnStackTrace\") != null;\n\n  private void go() {\n    if (!outDir.mkdirs() && !outDir.exists() || !outDir.isDirectory()) {\n      System.out.println(\"Unable to create output directory: \" + outDir);\n      System.exit(1);\n    }\n    BasicProgram prog = parse();\n    WellTypedProgram typedProg = typeCheck(prog);\n    Evaluation eval = setup(typedProg);\n    Runtime.getRuntime()\n        .addShutdownHook(\n            new Thread(\n                () -> {\n                  if (interrupted) {\n                    dumpResults(eval.getResult());\n                  }\n                }));\n    evaluate(eval);\n    interrupted = false;\n    var res = eval.getResult();\n    dumpResults(res);\n    dumpResultsToDisk(res);\n    AbstractSmtLibSolver.destroyAll();\n  }\n\n  private BasicProgram parse() {\n    System.out.println(\"Parsing...\");\n    clock.start();\n    try {\n      List<Path> factPaths = factDirs.stream().map(Paths::get).collect(Collectors.toList());\n      if (factPaths.isEmpty()) {\n        factPaths = Collections.singletonList(Paths.get(\"\"));\n      }\n      FileReader reader = new FileReader(file);\n      BasicProgram prog = new Parser().parse(reader, factPaths);\n      clock.stop();\n      System.out.println(\"Finished parsing (\" + clock.getTime() / 1000.0 + \"s)\");\n      return prog;\n    } catch (FileNotFoundException e) {\n      handleException(\"Error while parsing!\", e);\n    } catch (ParseException e) {\n      String msg = \"Error while parsing \";\n      if (e.getFileName() != null) {\n        msg += e.getFileName() + \", \";\n      }\n      msg += \"line \" + e.getLineNo() + \":\";\n      handleException(msg, e);\n    }\n    throw new AssertionError(\"impossible\");\n  }\n\n  private WellTypedProgram typeCheck(Program<UserPredicate, BasicRule> prog) {\n    System.out.println(\"Type checking...\");\n    clock.reset();\n    clock.start();\n    try {\n      WellTypedProgram prog2 = new TypeChecker(prog).typeCheck();\n      clock.stop();\n      System.out.println(\"Finished type checking (\" + clock.getTime() / 1000.0 + \"s)\");\n      return prog2;\n    } catch (TypeException e) {\n      handleException(\"Error while typechecking the program!\", e);\n      throw new AssertionError(\"impossible\");\n    }\n  }\n\n  private Evaluation setup(WellTypedProgram prog) {\n    System.out.println(\"Rewriting and validating...\");\n    clock.reset();\n    clock.start();\n    try {\n      Evaluation eval = SemiNaiveEvaluation.setup(prog, parallelism, eagerEval);\n      clock.stop();\n      System.out.println(\"Finished rewriting and validating (\" + clock.getTime() / 1000.0 + \"s)\");\n      return eval;\n    } catch (InvalidProgramException e) {\n      handleException(\"Error while rewriting/validation!\", e);\n      throw new AssertionError(\"impossible\");\n    }\n  }\n\n  private void evaluate(Evaluation eval) {\n    System.out.println(\"Evaluating...\");\n    clock.reset();\n    clock.start();\n    try {\n      eval.run();\n      clock.stop();\n      System.out.println(\"Finished evaluating (\" + clock.getTime() / 1000.0 + \"s)\");\n    } catch (EvaluationException e) {\n      handleException(\"Error while evaluating the program!\", e);\n    }\n  }\n\n  private String getBanner(String heading) {\n    return \"==================== \" + heading + \" ====================\";\n  }\n\n  private String getSmallBanner(String heading) {\n    return \"---------- \" + heading + \" ----------\";\n  }\n\n  private static class FactFileDumper implements Runnable {\n\n    private final Iterable<UserPredicate> facts;\n    private final PrintStream out;\n\n    public FactFileDumper(Iterable<UserPredicate> facts, PrintStream out) {\n      this.facts = facts;\n      this.out = out;\n    }\n\n    @Override\n    public void run() {\n      for (var fact : facts) {\n        Term[] args = fact.getArgs();\n        for (int i = 0; i < args.length; ++i) {\n          out.print(args[i]);\n          if (i < args.length - 1) {\n            out.print(\"\\t\");\n          }\n        }\n        out.println();\n      }\n      out.close();\n    }\n  }\n\n  private void dumpResultsToDisk(EvaluationResult res) {\n    var pool = Executors.newFixedThreadPool(parallelism);\n    try {\n      for (RelationSymbol sym : res.getSymbols()) {\n        if (sym.isIdbSymbol() && sym.isDisk()) {\n          File outFile = outDir.toPath().resolve(sym + \".tsv\").toFile();\n          boolean ok = outFile.createNewFile();\n          if (!ok && !outFile.exists()) {\n            throw new IOException(\"Cannot create output file \" + outFile.getName());\n          }\n          pool.submit(new FactFileDumper(res.getAll(sym), new PrintStream(outFile)));\n        }\n      }\n      pool.shutdown();\n      while (!pool.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS)) {\n        // do nothing\n      }\n    } catch (Exception e) {\n      handleException(\"Problem writing output to disk\", e);\n    }\n  }\n\n  private void printSmtStats(PrintStream out) {\n    List<String> times = new ArrayList<>();\n    List<String> calls = new ArrayList<>();\n    long[] total_time = {0};\n    long[] total_calls = {0};\n    Configuration.smtTime.forEach(\n        stats -> {\n          times.add(Long.toString(stats.time));\n          total_time[0] += stats.time;\n          calls.add(Long.toString(stats.ncalls));\n          total_calls[0] += stats.ncalls;\n        });\n\n    out.println();\n    out.println(getBanner(\"SMT STATS\"));\n    out.println(\"SMT calls: \" + total_calls[0]);\n    out.println(\"SMT time (ms): \" + total_time[0]);\n    out.println(\"SMT wait time (ms): \" + Configuration.smtWaitTime.unsafeGet() / 1e6);\n    out.println(\"SMT cache hits: \" + Configuration.smtCacheHits.unsafeGet());\n    out.println(\"SMT cache misses: \" + Configuration.smtCacheMisses.unsafeGet());\n    out.println(\"SMT cache clears: \" + Configuration.smtCacheClears.unsafeGet());\n    out.println(\"SMT calls per solver: \" + String.join(\",\", calls));\n    out.println(\"SMT time per solver (ms): \" + String.join(\",\", times));\n  }\n\n  private void dumpResults(EvaluationResult res) {\n    PrintStream out = System.out;\n    if (smtStats) {\n      printSmtStats(out);\n    }\n    List<RelationSymbol> allSymbols =\n        res.getSymbols().stream()\n            .sorted(Comparator.comparing(Object::toString))\n            .collect(Collectors.toList());\n    Set<String> stringReprs =\n        allSymbols.stream().map(RelationSymbol::toString).collect(Collectors.toSet());\n    for (String sym : relationsToPrint) {\n      if (!stringReprs.contains(sym)) {\n        out.println(\"\\nWARNING: ignoring unrecognized relation \" + sym);\n      }\n    }\n    if (dumpSizes) {\n      out.println();\n      out.println(getBanner(\"RELATION SIZES\"));\n      for (RelationSymbol sym : allSymbols) {\n        out.println(sym + \": \" + res.getCount(sym));\n      }\n    }\n    if (dumpAll || dumpQuery) {\n      var queryRes = res.getQueryAnswer();\n      if (queryRes != null) {\n        List<UserPredicate> l = new ArrayList<>();\n        queryRes.forEach(l::add);\n        out.println();\n        out.println(getBanner(\"QUERY RESULTS (\" + l.size() + \")\"));\n        Util.printSortedFacts(l, out);\n      }\n    }\n    List<RelationSymbol> edbSymbols =\n        allSymbols.stream()\n            .filter(\n                sym -> sym.isEdbSymbol() && (dumpAll || relationsToPrint.contains(sym.toString())))\n            .collect(Collectors.toList());\n    dumpRelations(\"SELECTED EDB RELATIONS\", edbSymbols, res, out);\n    List<RelationSymbol> idbSymbols =\n        allSymbols.stream()\n            .filter(\n                sym ->\n                    sym.isIdbSymbol()\n                        && (dumpAll || dumpIdb || relationsToPrint.contains(sym.toString())))\n            .collect(Collectors.toList());\n    dumpRelations(\"SELECTED IDB RELATIONS\", idbSymbols, res, out);\n  }\n\n  private void dumpRelations(\n      String heading, Collection<RelationSymbol> symbols, EvaluationResult res, PrintStream out) {\n    if (symbols.isEmpty()) {\n      return;\n    }\n    out.println();\n    out.println(getBanner(heading));\n    for (RelationSymbol sym : symbols) {\n      out.println();\n      out.println(getSmallBanner(sym + \" (\" + res.getCount(sym) + \")\"));\n      if (res.getCount(sym) < 1000) {\n        Util.printSortedFacts(res.getAll(sym), out);\n      } else {\n        for (var tup : res.getAll(sym)) {\n          out.println(tup);\n        }\n      }\n    }\n  }\n\n  public static class SmtModeConverter implements ITypeConverter<SmtStrategy> {\n\n    @Override\n    public SmtStrategy convert(String s) throws Exception {\n      switch (s) {\n        case \"push-pop\":\n          return new SmtStrategy(SmtStrategy.Tag.PER_THREAD_PUSH_POP, null);\n        case \"naive\":\n          return new SmtStrategy(SmtStrategy.Tag.PER_THREAD_PUSH_POP_NAIVE, null);\n        case \"check-sat-assuming\":\n          return new SmtStrategy(SmtStrategy.Tag.PER_THREAD_QUEUE, 1);\n      }\n      throw new ParameterException(\n          Main.spec.commandLine(),\n          \"Unexpected value for SMT solver mode: \"\n              + s\n              + \" (must be one of 'naive', 'push-pop', or 'check-sat-assuming')\");\n    }\n  }\n\n  public static void main(String[] args) throws Exception {\n    int exitCode = new CommandLine(new Main()).execute(args);\n    System.exit(exitCode);\n  }\n\n  private static void handleException(String msg, Exception e) {\n    System.out.println(msg);\n    System.out.println(e.getMessage());\n    if (exnStackTrace) {\n      e.printStackTrace(System.out);\n    }\n    System.exit(1);\n  }\n\n  @Override\n  public Integer call() throws Exception {\n    if (codegen) {\n      CodeGen.main(file, codegenDir);\n    } else {\n      go();\n    }\n    return 0;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/PrintPreference.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog;\n\npublic enum PrintPreference {\n  EDB,\n  IDB,\n  NONE,\n  ALL,\n  QUERY,\n  SOME\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/ast/AbstractRule.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.ast;\n\nimport java.util.Iterator;\nimport java.util.List;\n\npublic abstract class AbstractRule<H extends Literal, B extends Literal> implements Rule<H, B> {\n\n  private final H head;\n  private final List<B> body;\n\n  protected AbstractRule(H head, List<B> body) {\n    this.head = head;\n    this.body = body;\n  }\n\n  @Override\n  public Iterator<B> iterator() {\n    return body.iterator();\n  }\n\n  @Override\n  public H getHead() {\n    return head;\n  }\n\n  @Override\n  public int getBodySize() {\n    return body.size();\n  }\n\n  @Override\n  public B getBody(int idx) {\n    return body.get(idx);\n  }\n\n  @Override\n  public String toString() {\n    StringBuilder sb = new StringBuilder();\n    sb.append(head);\n    sb.append(\" :-\");\n    if (body.size() == 1) {\n      sb.append(\" \");\n    } else {\n      sb.append(\"\\n\\t\");\n    }\n    for (Iterator<B> it = body.iterator(); it.hasNext(); ) {\n      sb.append(it.next());\n      if (it.hasNext()) {\n        sb.append(\",\\n\\t\");\n      }\n    }\n    sb.append(\".\");\n    return sb.toString();\n  }\n\n  @Override\n  public int hashCode() {\n    final int prime = 31;\n    int result = 1;\n    result = prime * result + ((body == null) ? 0 : body.hashCode());\n    result = prime * result + ((head == null) ? 0 : head.hashCode());\n    return result;\n  }\n\n  @Override\n  public boolean equals(Object obj) {\n    if (this == obj) return true;\n    if (obj == null) return false;\n    if (getClass() != obj.getClass()) return false;\n    AbstractRule<?, ?> other = (AbstractRule<?, ?>) obj;\n    if (body == null) {\n      if (other.body != null) return false;\n    } else if (!body.equals(other.body)) return false;\n    if (head == null) {\n      if (other.head != null) return false;\n    } else if (!head.equals(other.head)) return false;\n    return true;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/ast/AbstractTerm.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.ast;\n\npublic abstract class AbstractTerm implements Term {\n\n  private final int id;\n\n  public AbstractTerm() {\n    this.id = Terms.nextId();\n  }\n\n  @Override\n  public int getId() {\n    return id;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/ast/BasicProgram.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.ast;\n\npublic interface BasicProgram extends Program<UserPredicate, BasicRule> {}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/ast/BasicRule.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.ast;\n\nimport java.util.Collections;\nimport java.util.List;\n\npublic class BasicRule extends AbstractRule<UserPredicate, ComplexLiteral> {\n\n  private BasicRule(UserPredicate head, List<ComplexLiteral> body) {\n    super(head, body);\n    if (!head.getSymbol().isIdbSymbol()) {\n      throw new IllegalArgumentException(\n          \"Cannot create rule with non-IDB predicate for head: \" + head.getSymbol());\n    }\n  }\n\n  public static BasicRule make(UserPredicate head, List<ComplexLiteral> body) {\n    if (body.isEmpty()) {\n      return make(head);\n    }\n    return new BasicRule(head, body);\n  }\n\n  public static BasicRule make(UserPredicate head) {\n    return make(head, Collections.singletonList(ComplexLiterals.trueAtom()));\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/ast/BindingType.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.ast;\n\npublic enum BindingType {\n  BOUND,\n  FREE,\n  IGNORED;\n\n  public boolean isBound() {\n    return this.equals(BOUND);\n  }\n\n  public boolean isFree() {\n    return this.equals(FREE);\n  }\n\n  public boolean isIgnored() {\n    return this.equals(IGNORED);\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/ast/BoolTerm.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.ast;\n\nimport edu.harvard.seas.pl.formulog.ast.Constructors.SolverVariable;\nimport edu.harvard.seas.pl.formulog.smt.SmtLibShim;\nimport edu.harvard.seas.pl.formulog.types.BuiltInTypes;\nimport edu.harvard.seas.pl.formulog.types.Types.Type;\nimport java.util.Collections;\nimport java.util.Set;\nimport org.pcollections.PMap;\n\npublic class BoolTerm extends AbstractTerm implements Primitive<Boolean>, SmtLibTerm {\n\n  private final boolean val;\n  private static final BoolTerm true_ = new BoolTerm(true);\n  private static final BoolTerm false_ = new BoolTerm(false);\n\n  private BoolTerm(boolean val) {\n    this.val = val;\n  }\n\n  public static BoolTerm mk(boolean val) {\n    return val ? true_ : false_;\n  }\n\n  public static BoolTerm mkTrue() {\n    return true_;\n  }\n\n  public static BoolTerm mkFalse() {\n    return false_;\n  }\n\n  @Override\n  public Boolean getVal() {\n    return val;\n  }\n\n  @Override\n  public Type getType() {\n    return BuiltInTypes.bool;\n  }\n\n  @Override\n  public void toSmtLib(SmtLibShim shim) {\n    shim.print(this.toString());\n  }\n\n  @Override\n  public SmtLibTerm substSolverTerms(PMap<SolverVariable, SmtLibTerm> subst) {\n    return this;\n  }\n\n  @Override\n  public Set<SolverVariable> freeVars() {\n    return Collections.emptySet();\n  }\n\n  @Override\n  public String toString() {\n    return Boolean.toString(val);\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/ast/ComplexLiteral.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.ast;\n\nimport edu.harvard.seas.pl.formulog.ast.ComplexLiterals.ComplexLiteralExnVisitor;\nimport edu.harvard.seas.pl.formulog.ast.ComplexLiterals.ComplexLiteralVisitor;\nimport edu.harvard.seas.pl.formulog.unification.Substitution;\nimport java.util.HashSet;\nimport java.util.Set;\n\npublic interface ComplexLiteral extends Literal {\n\n  Term[] getArgs();\n\n  ComplexLiteral applySubstitution(Substitution subst);\n\n  boolean isNegated();\n\n  default Set<Var> varSet() {\n    Set<Var> vars = new HashSet<>();\n    for (Term arg : getArgs()) {\n      arg.varSet(vars);\n    }\n    return vars;\n  }\n\n  <I, O> O accept(ComplexLiteralVisitor<I, O> visitor, I input);\n\n  <I, O, E extends Throwable> O accept(ComplexLiteralExnVisitor<I, O, E> visitor, I input) throws E;\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/ast/ComplexLiterals.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.ast;\n\npublic final class ComplexLiterals {\n\n  private ComplexLiterals() {\n    throw new AssertionError(\"impossible\");\n  }\n\n  public static UnificationPredicate unifyWithBool(Term t, boolean bool) {\n    return UnificationPredicate.make(t, bool ? BoolTerm.mkTrue() : BoolTerm.mkFalse(), false);\n  }\n\n  public static UnificationPredicate trueAtom() {\n    return unifyWithBool(BoolTerm.mkTrue(), true);\n  }\n\n  public static interface ComplexLiteralVisitor<I, O> {\n\n    O visit(UnificationPredicate unificationPredicate, I input);\n\n    O visit(UserPredicate userPredicate, I input);\n  }\n\n  public static interface ComplexLiteralExnVisitor<I, O, E extends Throwable> {\n\n    O visit(UnificationPredicate unificationPredicate, I input) throws E;\n\n    O visit(UserPredicate userPredicate, I input) throws E;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/ast/Constructor.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.ast;\n\nimport edu.harvard.seas.pl.formulog.ast.Terms.TermVisitor;\nimport edu.harvard.seas.pl.formulog.ast.Terms.TermVisitorExn;\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport edu.harvard.seas.pl.formulog.symbols.ConstructorSymbol;\nimport edu.harvard.seas.pl.formulog.unification.Substitution;\n\npublic interface Constructor extends Functor<ConstructorSymbol>, SmtLibTerm {\n\n  @Override\n  default <I, O> O accept(TermVisitor<I, O> v, I in) {\n    return v.visit(this, in);\n  }\n\n  @Override\n  default <I, O, E extends Throwable> O accept(TermVisitorExn<I, O, E> v, I in) throws E {\n    return v.visit(this, in);\n  }\n\n  @Override\n  default Term applySubstitution(Substitution s) {\n    if (isGround()) {\n      return this;\n    }\n    Term[] args = getArgs();\n    Term[] newArgs = new Term[args.length];\n    for (int i = 0; i < args.length; ++i) {\n      newArgs[i] = args[i].applySubstitution(s);\n    }\n    return copyWithNewArgs(newArgs);\n  }\n\n  @Override\n  Term copyWithNewArgs(Term[] newArgs);\n\n  @Override\n  default Term normalize(Substitution s) throws EvaluationException {\n    if (isGround() && !containsUnevaluatedTerm()) {\n      return this;\n    }\n    Term[] args = getArgs();\n    Term[] newArgs = new Term[args.length];\n    for (int i = 0; i < args.length; ++i) {\n      newArgs[i] = args[i].normalize(s);\n    }\n    return copyWithNewArgs(newArgs);\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/ast/Constructors.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.ast;\n\nimport edu.harvard.seas.pl.formulog.Configuration;\nimport edu.harvard.seas.pl.formulog.smt.SmtLibShim;\nimport edu.harvard.seas.pl.formulog.symbols.BuiltInConstructorSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.ConstructorSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.FunctionSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.GlobalSymbolManager;\nimport edu.harvard.seas.pl.formulog.symbols.GlobalSymbolManager.TupleSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.RecordSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.parameterized.BuiltInConstructorSymbolBase;\nimport edu.harvard.seas.pl.formulog.symbols.parameterized.Param;\nimport edu.harvard.seas.pl.formulog.symbols.parameterized.ParameterizedConstructorSymbol;\nimport edu.harvard.seas.pl.formulog.types.FunctorType;\nimport edu.harvard.seas.pl.formulog.types.Types.AlgebraicDataType;\nimport edu.harvard.seas.pl.formulog.types.Types.Type;\nimport edu.harvard.seas.pl.formulog.types.Types.TypeIndex;\nimport edu.harvard.seas.pl.formulog.util.FunctorUtil;\nimport edu.harvard.seas.pl.formulog.util.FunctorUtil.Memoizer;\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.function.Function;\nimport org.pcollections.PMap;\n\npublic final class Constructors {\n\n  private Constructors() {\n    throw new AssertionError();\n  }\n\n  private static final Memoizer<Constructor> memo = new Memoizer<>();\n\n  public static Constructor make(ConstructorSymbol sym, Term[] args) {\n    assert sym.getArity() == args.length : sym + \" \" + Arrays.toString(args);\n    if (sym instanceof BuiltInConstructorSymbol) {\n      return lookupOrCreateBuiltInConstructor((BuiltInConstructorSymbol) sym, args);\n    }\n    if (sym instanceof ParameterizedConstructorSymbol) {\n      return lookupOrCreateParameterizedConstructor((ParameterizedConstructorSymbol) sym, args);\n    }\n    if (sym instanceof TupleSymbol) {\n      return memo.lookupOrCreate(sym, args, () -> new Tuple((TupleSymbol) sym, args));\n    }\n    if (sym instanceof RecordSymbol) {\n      return memo.lookupOrCreate(sym, args, () -> new Record((RecordSymbol) sym, args));\n    }\n    switch (sym.getConstructorSymbolType()) {\n      case SOLVER_UNINTERPRETED_FUNCTION:\n        return memo.lookupOrCreate(sym, args, () -> new SolverUninterpretedFunction(sym, args));\n      case SOLVER_CONSTRUCTOR_TESTER:\n        return memo.lookupOrCreate(sym, args, () -> makeConstructorTester(sym, args));\n      case SOLVER_CONSTRUCTOR_GETTER:\n        return memo.lookupOrCreate(sym, args, () -> makeConstructorGetter(sym, args));\n      case INDEX_CONSTRUCTOR:\n      case VANILLA_CONSTRUCTOR:\n        return memo.lookupOrCreate(sym, args, () -> new VanillaConstructor(sym, args));\n      default:\n        throw new IllegalArgumentException(\n            \"Cannot create constructor for non-constructor symbol \" + sym + \".\");\n    }\n  }\n\n  public static Constructor makeZeroAry(ConstructorSymbol sym) {\n    return make(sym, Terms.emptyArray());\n  }\n\n  private static final Constructor nil = makeZeroAry(BuiltInConstructorSymbol.NIL);\n\n  public static Constructor nil() {\n    return nil;\n  }\n\n  public static Constructor cons(Term hd, Term tl) {\n    return make(BuiltInConstructorSymbol.CONS, new Term[] {hd, tl});\n  }\n\n  private static final Constructor none = makeZeroAry(BuiltInConstructorSymbol.NONE);\n\n  public static final Term none() {\n    return none;\n  }\n\n  public static Term some(Term arg) {\n    return make(BuiltInConstructorSymbol.SOME, Terms.singletonArray(arg));\n  }\n\n  public static Term tuple(Term... args) {\n    return make(GlobalSymbolManager.lookupTupleSymbol(args.length), args);\n  }\n\n  private static Constructor lookupOrCreateBuiltInConstructor(\n      BuiltInConstructorSymbol sym, Term[] args) {\n    Function<String, Constructor> makeSolverOp =\n        op -> memo.lookupOrCreate(sym, args, () -> new SolverOperation(sym, args, op));\n    switch (sym) {\n      case NIL:\n        return makeNil(sym, args);\n      case CONS:\n        return makeCons(sym, args);\n      case CMP_EQ:\n      case CMP_GT:\n      case CMP_LT:\n      case NONE:\n      case SOME:\n        return memo.lookupOrCreate(sym, args, () -> new VanillaConstructor(sym, args));\n      case SMT_AND:\n        return makeAnd(args);\n      case SMT_IMP:\n        return makeSolverOp.apply(\"=>\");\n      case SMT_ITE:\n        return memo.lookupOrCreate(sym, args, () -> new SolverIte(sym, args));\n      case SMT_NOT:\n        return makeSolverOp.apply(\"not\");\n      case SMT_OR:\n        return makeOr(args);\n      case SMT_EXISTS:\n      case SMT_FORALL:\n        return memo.lookupOrCreate(sym, args, () -> new Quantifier(sym, args));\n      case BV_ADD:\n        return makeSolverOp.apply(\"bvadd\");\n      case BV_AND:\n        return makeSolverOp.apply(\"bvand\");\n      case BV_MUL:\n        return makeSolverOp.apply(\"bvmul\");\n      case BV_NEG:\n        return makeSolverOp.apply(\"bvneg\");\n      case BV_OR:\n        return makeSolverOp.apply(\"bvor\");\n      case BV_SDIV:\n        return makeSolverOp.apply(\"bvsdiv\");\n      case BV_UDIV:\n        return makeSolverOp.apply(\"bvudiv\");\n      case BV_SREM:\n        return makeSolverOp.apply(\"bvsrem\");\n      case BV_UREM:\n        return makeSolverOp.apply(\"bvurem\");\n      case BV_SUB:\n        return makeSolverOp.apply(\"bvsub\");\n      case BV_XOR:\n        return makeSolverOp.apply(\"bvxor\");\n      case BV_SHL:\n        return makeSolverOp.apply(\"bvshl\");\n      case BV_ASHR:\n        return makeSolverOp.apply(\"bvashr\");\n      case BV_LSHR:\n        return makeSolverOp.apply(\"bvlshr\");\n      case FP_ADD:\n        return makeSolverOp.apply(\"fp.add\");\n      case FP_DIV:\n        return makeSolverOp.apply(\"fp.div\");\n      case FP_MUL:\n        return makeSolverOp.apply(\"fp.mul\");\n      case FP_NEG:\n        return makeSolverOp.apply(\"fp.neg\");\n      case FP_REM:\n        return makeSolverOp.apply(\"fp.rem\");\n      case FP_SUB:\n        return makeSolverOp.apply(\"fp.sub\");\n      case ARRAY_STORE:\n        return makeSolverOp.apply(\"store\");\n      case ARRAY_CONST:\n        return makeSolverOp.apply(\"const\");\n      case INT_ABS:\n        return makeSolverOp.apply(\"abs\");\n      case INT_ADD:\n        return makeSolverOp.apply(\"+\");\n      case INT_BIG_CONST:\n      case INT_CONST:\n        return makeIntConst(sym, args);\n      case INT_DIV:\n        return makeSolverOp.apply(\"div\");\n      case INT_GE:\n        return makeSolverOp.apply(\">=\");\n      case INT_GT:\n        return makeSolverOp.apply(\">\");\n      case INT_LE:\n        return makeSolverOp.apply(\"<=\");\n      case INT_LT:\n        return makeSolverOp.apply(\"<\");\n      case INT_MUL:\n        return makeSolverOp.apply(\"*\");\n      case INT_NEG:\n        return makeSolverOp.apply(\"-\");\n      case INT_MOD:\n        return makeSolverOp.apply(\"mod\");\n      case INT_SUB:\n        return makeSolverOp.apply(\"-\");\n      case STR_AT:\n        return makeSolverOp.apply(\"str.at\");\n      case STR_CONCAT:\n        return makeSolverOp.apply(\"str.++\");\n      case STR_CONTAINS:\n        return makeSolverOp.apply(\"str.contains\");\n      case STR_INDEXOF:\n        return makeSolverOp.apply(\"str.indexof\");\n      case STR_LEN:\n        return makeSolverOp.apply(\"str.len\");\n      case STR_PREFIXOF:\n        return makeSolverOp.apply(\"str.prefixof\");\n      case STR_REPLACE:\n        return makeSolverOp.apply(\"str.replace\");\n      case STR_SUBSTR:\n        return makeSolverOp.apply(\"str.substr\");\n      case STR_SUFFIXOF:\n        return makeSolverOp.apply(\"str.suffixof\");\n      case ENTER_FORMULA:\n        return memo.lookupOrCreate(sym, args, () -> new VanillaConstructor(sym, args));\n      case EXIT_FORMULA:\n        return memo.lookupOrCreate(sym, args, () -> new VanillaConstructor(sym, args));\n    }\n    throw new AssertionError(\"impossible\");\n  }\n\n  private static Constructor makeAnd(Term[] args) {\n    ConstructorSymbol sym = BuiltInConstructorSymbol.SMT_AND;\n    return memo.lookupOrCreate(\n        sym,\n        args,\n        () -> {\n          return new SolverOperation(sym, args, \"and\");\n        });\n  }\n\n  private static Constructor makeOr(Term[] args) {\n    ConstructorSymbol sym = BuiltInConstructorSymbol.SMT_OR;\n    return memo.lookupOrCreate(\n        sym,\n        args,\n        () -> {\n          return new SolverOperation(sym, args, \"or\");\n        });\n  }\n\n  // Used for renaming variables to avoid capture.\n  private static final Map<PMap<SolverVariable, SmtLibTerm>, SolverVariable> binderMemo =\n      new ConcurrentHashMap<>();\n\n  private static final AtomicInteger cnt = new AtomicInteger();\n\n  private static SolverVariable renameBinder(SolverVariable x) {\n    ParameterizedConstructorSymbol sym = x.getSymbol();\n    sym = sym.copyWithNewArgs(Param.wildCard(), sym.getArgs().get(1));\n    return (SolverVariable)\n        make(sym, Terms.singletonArray(Terms.makeDummyTerm(cnt.getAndIncrement())));\n  }\n\n  private static class SolverLet extends AbstractConstructor<ConstructorSymbol> {\n\n    public static Constructor make(ConstructorSymbol sym, Term[] args) {\n      return memo.lookupOrCreate(\n          sym,\n          args,\n          () -> {\n            return new SolverLet(sym, args);\n          });\n    }\n\n    private SolverLet(ConstructorSymbol sym, Term[] args) {\n      super(sym, args);\n    }\n\n    @Override\n    public void toSmtLib(SmtLibShim shim) {\n      shim.print(\"(let ((\");\n      ((SmtLibTerm) args[0]).toSmtLib(shim);\n      shim.print(\" \");\n      ((SmtLibTerm) args[1]).toSmtLib(shim);\n      shim.print(\")) \");\n      ((SmtLibTerm) args[2]).toSmtLib(shim);\n      shim.print(\")\");\n    }\n\n    @Override\n    public SmtLibTerm substSolverTerms(PMap<SolverVariable, SmtLibTerm> subst) {\n      // Rename bound variable to avoid variable capture.\n      SolverVariable x = (SolverVariable) args[0];\n      SolverVariable y = Util.lookupOrCreate(binderMemo, subst, () -> renameBinder(x));\n      Term[] newArgs = new Term[args.length];\n      newArgs[0] = y;\n      newArgs[1] = ((SmtLibTerm) args[1]).substSolverTerms(subst);\n      newArgs[2] = ((SmtLibTerm) args[2]).substSolverTerms(subst.plus(x, y));\n      return (SmtLibTerm) copyWithNewArgs(newArgs);\n    }\n\n    @Override\n    public String toString() {\n      return \"(#let \" + args[0] + \" = \" + args[1] + \" in \" + args[2] + \")\";\n    }\n\n    @Override\n    public Set<SolverVariable> freeVars() {\n      Set<SolverVariable> vars = new HashSet<>(((SmtLibTerm) args[2]).freeVars());\n      vars.remove((SolverVariable) args[0]);\n      vars.addAll(((SmtLibTerm) args[1]).freeVars());\n      return vars;\n    }\n  }\n\n  private static class Quantifier extends AbstractConstructor<BuiltInConstructorSymbol> {\n\n    protected Quantifier(BuiltInConstructorSymbol sym, Term[] args) {\n      super(sym, args);\n    }\n\n    private boolean isExists() {\n      return sym.equals(BuiltInConstructorSymbol.SMT_EXISTS);\n    }\n\n    @Override\n    public void toSmtLib(SmtLibShim shim) {\n      String quantifier = \"forall (\";\n      if (isExists()) {\n        quantifier = \"exists (\";\n      }\n      shim.print(\"(\");\n      shim.print(quantifier);\n      for (Term t : getBoundVars()) {\n        shim.getTypeAnnotation(BuiltInConstructorSymbol.CONS);\n        SolverVariable x = (SolverVariable) t;\n        shim.print(\"(\");\n        x.toSmtLib(shim);\n        shim.print(\" \");\n        FunctorType ft = (FunctorType) x.getSymbol().getCompileTimeType();\n        shim.print(ft.getRetType());\n        shim.print(\")\");\n      }\n      shim.print(\") \");\n      // Consume final type annotation for variable list\n      shim.getTypeAnnotation(BuiltInConstructorSymbol.NIL);\n      List<List<Term>> pats = getPatterns();\n      // XXX Need to check if pattern is valid!\n      if (!pats.isEmpty()) {\n        shim.print(\"(! \");\n      }\n      ((SmtLibTerm) args[1]).toSmtLib(shim);\n      if (!pats.isEmpty()) {\n        for (List<Term> pat : pats) {\n          shim.print(\" :pattern (\");\n          shim.getTypeAnnotation(BuiltInConstructorSymbol.CONS);\n          for (Iterator<Term> it = pat.iterator(); it.hasNext(); ) {\n            shim.getTypeAnnotation(BuiltInConstructorSymbol.CONS);\n            Constructor wrappedPat = (Constructor) it.next();\n            SmtLibTerm t = (SmtLibTerm) wrappedPat.getArgs()[0];\n            t.toSmtLib(shim);\n            if (it.hasNext()) {\n              shim.print(\" \");\n            }\n          }\n          shim.print(\")\");\n          // Consume final type annotation for pattern list\n          shim.getTypeAnnotation(BuiltInConstructorSymbol.NIL);\n        }\n        shim.print(\")\");\n      }\n      // Consume final type annotation for pattern list list\n      shim.getTypeAnnotation(BuiltInConstructorSymbol.NIL);\n      shim.print(\")\");\n    }\n\n    @Override\n    public SmtLibTerm substSolverTerms(PMap<SolverVariable, SmtLibTerm> subst) {\n      // Rename bound variable to avoid variable capture.\n      List<SolverVariable> newVars = new ArrayList<>();\n      PMap<SolverVariable, SmtLibTerm> newSubst = subst;\n      for (Term t : getBoundVars()) {\n        SolverVariable x = (SolverVariable) t;\n        SolverVariable y = Util.lookupOrCreate(binderMemo, subst, () -> renameBinder(x));\n        newVars.add(y);\n        newSubst = subst.plus(x, y);\n      }\n      Term[] newArgs = new Term[args.length];\n      for (int i = 0; i < args.length; ++i) {\n        newArgs[i] = ((SmtLibTerm) args[i]).substSolverTerms(newSubst);\n      }\n      return (SmtLibTerm) copyWithNewArgs(newArgs);\n    }\n\n    protected List<List<Term>> getPatterns() {\n      List<List<Term>> l = new ArrayList<>();\n      for (Term pat : Terms.termToTermList(args[2])) {\n        l.add(Terms.termToTermList(pat));\n      }\n      return l;\n    }\n\n    private List<SolverVariable> getBoundVars() {\n      List<Term> wrappedVars = Terms.termToTermList(args[0]);\n      List<SolverVariable> vars = new ArrayList<>();\n      for (Term wrappedVar : wrappedVars) {\n        SolverVariable var = (SolverVariable) ((Constructor) wrappedVar).getArgs()[0];\n        vars.add(var);\n      }\n      return vars;\n    }\n\n    @Override\n    public Set<SolverVariable> freeVars() {\n      Set<SolverVariable> vars = super.freeVars();\n      vars.removeAll(getBoundVars());\n      return vars;\n    }\n  }\n\n  private static class Nil extends AbstractConstructor<ConstructorSymbol> {\n\n    protected Nil(ConstructorSymbol sym, Term[] args) {\n      super(sym, args);\n    }\n\n    @Override\n    public void toSmtLib(SmtLibShim shim) {\n      Constructors.toSmtLib(this, shim);\n    }\n\n    @Override\n    public String toString() {\n      return \"[]\";\n    }\n  }\n\n  private static class Cons extends AbstractConstructor<ConstructorSymbol> {\n\n    protected Cons(ConstructorSymbol sym, Term[] args) {\n      super(sym, args);\n    }\n\n    @Override\n    public void toSmtLib(SmtLibShim shim) {\n      Constructors.toSmtLib(this, shim);\n    }\n\n    @Override\n    public String toString() {\n      List<Term> listArgs = new ArrayList<>();\n      Term cur = this;\n      while (cur instanceof Cons) {\n        Cons cons = (Cons) cur;\n        listArgs.add(cons.args[0]);\n        cur = cons.args[1];\n      }\n      if (cur instanceof Nil) {\n        String s = \"[\";\n        for (Iterator<Term> it = listArgs.iterator(); it.hasNext(); ) {\n          s += it.next();\n          if (it.hasNext()) {\n            s += \", \";\n          }\n        }\n        return s + \"]\";\n      } else {\n        String s = \"(\";\n        for (Term t : listArgs) {\n          s += t;\n          s += \" :: \";\n        }\n        return s + cur + \")\";\n      }\n    }\n  }\n\n  private static Constructor makeNil(ConstructorSymbol sym, Term[] args) {\n    return memo.lookupOrCreate(sym, args, () -> new Nil(sym, args));\n  }\n\n  private static Constructor makeCons(ConstructorSymbol sym, Term[] args) {\n    return memo.lookupOrCreate(sym, args, () -> new Cons(sym, args));\n  }\n\n  private static Constructor makeIntConst(ConstructorSymbol sym, Term[] args) {\n    return memo.lookupOrCreate(\n        sym,\n        args,\n        () ->\n            new AbstractConstructor<ConstructorSymbol>(sym, args) {\n\n              @Override\n              public void toSmtLib(SmtLibShim shim) {\n                String repr = ((Primitive<?>) args[0]).getVal().toString();\n                if (repr.charAt(0) == '-') {\n                  shim.print(\"(- \" + repr.substring(1, repr.length()) + \")\");\n                } else {\n                  shim.print(repr);\n                }\n              }\n            });\n  }\n\n  private static Constructor lookupOrCreateParameterizedConstructor(\n      ParameterizedConstructorSymbol sym, Term[] args) {\n    Function<String, Constructor> makeSolverOp =\n        op -> memo.lookupOrCreate(sym, args, () -> new SolverOperation(sym, args, op));\n    BuiltInConstructorSymbolBase preSym = sym.getBase();\n    switch (preSym) {\n      case SMT_EQ:\n        return makeSolverOp.apply(\"=\");\n      case SMT_LET:\n        return SolverLet.make(sym, args);\n      case ARRAY_DEFAULT:\n        return makeSolverOp.apply(\"default\");\n      case ARRAY_SELECT:\n        return makeSolverOp.apply(\"select\");\n      case BV_SGE:\n        return makeSolverOp.apply(\"bvsge\");\n      case BV_SGT:\n        return makeSolverOp.apply(\"bvsgt\");\n      case BV_SLE:\n        return makeSolverOp.apply(\"bvsle\");\n      case BV_SLT:\n        return makeSolverOp.apply(\"bvslt\");\n      case BV_UGE:\n        return makeSolverOp.apply(\"bvuge\");\n      case BV_UGT:\n        return makeSolverOp.apply(\"bvugt\");\n      case BV_ULE:\n        return makeSolverOp.apply(\"bvule\");\n      case BV_ULT:\n        return makeSolverOp.apply(\"bvult\");\n      case FP_EQ:\n        return makeSolverOp.apply(\"fp.eq\");\n      case FP_GE:\n        return makeSolverOp.apply(\"fp.geq\");\n      case FP_GT:\n        return makeSolverOp.apply(\"fp.gt\");\n      case FP_IS_NAN:\n        return makeSolverOp.apply(\"fp.isNaN\");\n      case FP_LE:\n        return makeSolverOp.apply(\"fp.leq\");\n      case FP_LT:\n        return makeSolverOp.apply(\"fp.lt\");\n      case BV_TO_BV_SIGNED:\n        return makeBvToBvSigned(sym, args);\n      case BV_TO_BV_UNSIGNED:\n        return makeBvToBvUnsigned(sym, args);\n      case FP_TO_FP:\n        return makeFpToFp(sym, args);\n      case BV_TO_FP:\n        return makeBvToFp(sym, args);\n      case FP_TO_SBV:\n        return makeFpToBv(sym, args, true);\n      case FP_TO_UBV:\n        return makeFpToBv(sym, args, false);\n      case BV_CONST:\n        return makeBvConst(sym, args);\n      case BV_BIG_CONST:\n        return makeBvBigConst(sym, args);\n      case INT_TO_BV:\n        return makeIntToBv(sym, args);\n      case BV_TO_INT:\n        return makeSolverOp.apply(\"bv2int\");\n      case BV_CONCAT:\n        return makeSolverOp.apply(\"concat\");\n      case BV_EXTRACT:\n        return makeBvExtract(sym, args);\n      case FP_BIG_CONST:\n      case FP_CONST:\n        return makeConstant(sym, args);\n      case SMT_PAT:\n      case SMT_WRAP_VAR:\n        return memo.lookupOrCreate(sym, args, () -> new VanillaConstructor(sym, args));\n      case SMT_VAR:\n        return memo.lookupOrCreate(sym, args, () -> new SolverVariable(sym, args));\n    }\n    throw new AssertionError(\"impossible\");\n  }\n\n  private static int nat(Param param) {\n    return ((TypeIndex) param.getType()).getIndex();\n  }\n\n  private static int nat(ParameterizedConstructorSymbol sym, int idx) {\n    return nat(sym.getArgs().get(idx));\n  }\n\n  private static Constructor makeBvConst(ParameterizedConstructorSymbol sym, Term[] args) {\n    return memo.lookupOrCreate(\n        sym,\n        args,\n        () ->\n            new AbstractConstructor<ParameterizedConstructorSymbol>(sym, args) {\n\n              @Override\n              public void toSmtLib(SmtLibShim shim) {\n                I32 arg = (I32) args[0];\n                int width = nat(sym.getArgs().get(0));\n                String s = Integer.toBinaryString(arg.getVal());\n                shim.print(formatBitString(s, width));\n              }\n            });\n  }\n\n  private static Constructor makeBvBigConst(ParameterizedConstructorSymbol sym, Term[] args) {\n    return memo.lookupOrCreate(\n        sym,\n        args,\n        () ->\n            new AbstractConstructor<ParameterizedConstructorSymbol>(sym, args) {\n\n              @Override\n              public void toSmtLib(SmtLibShim shim) {\n                I64 arg = (I64) args[0];\n                int width = nat(sym.getArgs().get(0));\n                String s = Long.toBinaryString(arg.getVal());\n                shim.print(formatBitString(s, width));\n              }\n            });\n  }\n\n  private static String formatBitString(String bitString, int width) {\n    int len = bitString.length();\n    if (width > len) {\n      String pad = \"\";\n      for (int w = len; w < width; w++) {\n        pad += \"0\";\n      }\n      bitString = pad + bitString;\n    } else if (width < len) {\n      bitString = bitString.substring(len - width, len);\n    }\n    return \"#b\" + bitString;\n  }\n\n  private static Constructor makeIntToBv(ParameterizedConstructorSymbol sym, Term[] args) {\n    return memo.lookupOrCreate(\n        sym,\n        args,\n        () ->\n            new AbstractConstructor<ParameterizedConstructorSymbol>(sym, args) {\n              @Override\n              public void toSmtLib(SmtLibShim shim) {\n                shim.print(\"((_ int2bv \");\n                int width = nat(sym.getArgs().get(0));\n                shim.print(width + \") \");\n                ((SmtLibTerm) args[0]).toSmtLib(shim);\n                shim.print(\")\");\n              }\n            });\n  }\n\n  private static Constructor makeBvExtract(ParameterizedConstructorSymbol sym, Term[] args) {\n    return memo.lookupOrCreate(\n        sym,\n        args,\n        () ->\n            new AbstractConstructor<ParameterizedConstructorSymbol>(sym, args) {\n              @Override\n              public void toSmtLib(SmtLibShim shim) {\n                shim.print(\"((_ extract \");\n                shim.print(args[2] + \" \" + args[1] + \") \");\n                ((SmtLibTerm) args[0]).toSmtLib(shim);\n                shim.print(\")\");\n              }\n            });\n  }\n\n  private static Constructor makeConstant(ConstructorSymbol sym, Term[] args) {\n    return memo.lookupOrCreate(\n        sym,\n        args,\n        () ->\n            new AbstractConstructor<ConstructorSymbol>(sym, args) {\n\n              @Override\n              public void toSmtLib(SmtLibShim shim) {\n                ((SmtLibTerm) args[0]).toSmtLib(shim);\n              }\n            });\n  }\n\n  private static Constructor makeBvToBvSigned(ParameterizedConstructorSymbol sym, Term[] args) {\n    return memo.lookupOrCreate(\n        sym,\n        args,\n        () ->\n            new AbstractConstructor<ParameterizedConstructorSymbol>(sym, args) {\n\n              @Override\n              public void toSmtLib(SmtLibShim shim) {\n                int idx1 = nat(sym, 0);\n                int idx2 = nat(sym, 1);\n                SmtLibTerm t = (SmtLibTerm) args[0];\n                if (idx1 < idx2) {\n                  shim.print(\"(\");\n                  shim.print(\"(_ sign_extend \" + (idx2 - idx1) + \") \");\n                  t.toSmtLib(shim);\n                  shim.print(\")\");\n                } else if (idx1 == idx2) {\n                  t.toSmtLib(shim);\n                } else {\n                  shim.print(\"(\");\n                  shim.print(\"(_ extract \" + (idx2 - 1) + \" 0) \");\n                  t.toSmtLib(shim);\n                  shim.print(\")\");\n                }\n              }\n            });\n  }\n\n  private static Constructor makeBvToBvUnsigned(ParameterizedConstructorSymbol sym, Term[] args) {\n    return memo.lookupOrCreate(\n        sym,\n        args,\n        () ->\n            new AbstractConstructor<ParameterizedConstructorSymbol>(sym, args) {\n\n              @Override\n              public void toSmtLib(SmtLibShim shim) {\n                int idx1 = nat(sym, 0);\n                int idx2 = nat(sym, 1);\n                SmtLibTerm t = (SmtLibTerm) args[0];\n                if (idx1 < idx2) {\n                  shim.print(\"(\");\n                  shim.print(\"(_ zero_extend \" + (idx2 - idx1) + \") \");\n                  t.toSmtLib(shim);\n                  shim.print(\")\");\n                } else if (idx1 == idx2) {\n                  t.toSmtLib(shim);\n                } else {\n                  shim.print(\"(\");\n                  shim.print(\"(_ extract \" + (idx2 - 1) + \" 0) \");\n                  t.toSmtLib(shim);\n                  shim.print(\")\");\n                }\n              }\n            });\n  }\n\n  private static Constructor makeBvToFp(ParameterizedConstructorSymbol sym, Term[] args) {\n    return memo.lookupOrCreate(\n        sym,\n        args,\n        () ->\n            new AbstractConstructor<ParameterizedConstructorSymbol>(sym, args) {\n\n              @Override\n              public void toSmtLib(SmtLibShim shim) {\n                int exponent = nat(sym, 1);\n                int significand = nat(sym, 2);\n                shim.print(\"((_ to_fp \" + exponent + \" \" + significand + \") RNE \");\n                ((SmtLibTerm) args[0]).toSmtLib(shim);\n                shim.print(\")\");\n              }\n            });\n  }\n\n  private static Constructor makeFpToFp(ParameterizedConstructorSymbol sym, Term[] args) {\n    return memo.lookupOrCreate(\n        sym,\n        args,\n        () ->\n            new AbstractConstructor<ParameterizedConstructorSymbol>(sym, args) {\n\n              @Override\n              public void toSmtLib(SmtLibShim shim) {\n                int exponent = nat(sym, 2);\n                int significand = nat(sym, 3);\n                shim.print(\"((_ to_fp \" + exponent + \" \" + significand + \") RNE \");\n                ((SmtLibTerm) args[0]).toSmtLib(shim);\n                shim.print(\")\");\n              }\n            });\n  }\n\n  private static Constructor makeFpToBv(\n      ParameterizedConstructorSymbol sym, Term[] args, boolean signed) {\n    String s = signed ? \"fp.to_sbv\" : \"fp.to_ubv\";\n    return memo.lookupOrCreate(\n        sym,\n        args,\n        () ->\n            new AbstractConstructor<ParameterizedConstructorSymbol>(sym, args) {\n\n              @Override\n              public void toSmtLib(SmtLibShim shim) {\n                int width = nat(sym, 2);\n                shim.print(\"((_ \" + s + \" \" + width + \") RNE \");\n                ((SmtLibTerm) args[0]).toSmtLib(shim);\n                shim.print(\")\");\n              }\n            });\n  }\n\n  private abstract static class AbstractConstructor<S extends ConstructorSymbol>\n      extends AbstractTerm implements Constructor {\n\n    protected final S sym;\n    protected final Term[] args;\n    protected final boolean isGround;\n    protected final boolean containsFunctionCall;\n\n    protected AbstractConstructor(S sym, Term[] args) {\n      assert noneNull(args) : sym;\n      this.sym = sym;\n      this.args = args;\n      boolean g = true;\n      boolean f = false;\n      for (Term t : args) {\n        g &= t.isGround();\n        f |= t.containsUnevaluatedTerm();\n      }\n      isGround = g;\n      containsFunctionCall = f;\n    }\n\n    private boolean noneNull(Term[] ts) {\n      for (Term t : ts) {\n        if (t == null) {\n          return false;\n        }\n      }\n      return true;\n    }\n\n    @Override\n    public boolean isGround() {\n      return isGround;\n    }\n\n    @Override\n    public boolean containsUnevaluatedTerm() {\n      return containsFunctionCall;\n    }\n\n    @Override\n    public S getSymbol() {\n      return sym;\n    }\n\n    @Override\n    public Term[] getArgs() {\n      return args;\n    }\n\n    @Override\n    public String toString() {\n      return FunctorUtil.toString(sym, args);\n    }\n\n    @Override\n    public Term copyWithNewArgs(Term[] args) {\n      return make(sym, args);\n    }\n\n    @Override\n    public SmtLibTerm substSolverTerms(PMap<SolverVariable, SmtLibTerm> subst) {\n      if (subst.containsKey(this)) {\n        return subst.get(this);\n      }\n      Term[] newArgs = new Term[args.length];\n      for (int i = 0; i < args.length; ++i) {\n        newArgs[i] = ((SmtLibTerm) args[i]).substSolverTerms(subst);\n      }\n      return (SmtLibTerm) copyWithNewArgs(newArgs);\n    }\n\n    @Override\n    public Set<SolverVariable> freeVars() {\n      Set<SolverVariable> vars = new HashSet<>();\n      for (Term t : args) {\n        vars.addAll(((SmtLibTerm) t).freeVars());\n      }\n      return vars;\n    }\n  }\n\n  public static class VanillaConstructor extends AbstractConstructor<ConstructorSymbol> {\n\n    private VanillaConstructor(ConstructorSymbol sym, Term[] args) {\n      super(sym, args);\n    }\n\n    @Override\n    public void toSmtLib(SmtLibShim shim) {\n      Constructors.toSmtLib(this, shim);\n    }\n  }\n\n  public static class Tuple extends AbstractConstructor<TupleSymbol> {\n\n    private Tuple(TupleSymbol sym, Term[] args) {\n      super(sym, args);\n    }\n\n    @Override\n    public void toSmtLib(SmtLibShim shim) {\n      Constructors.toSmtLib(this, shim);\n    }\n\n    @Override\n    public String toString() {\n      StringBuilder sb = new StringBuilder(\"(\");\n      for (int i = 0; i < args.length; ++i) {\n        sb.append(args[i]);\n        if (i < args.length - 1) {\n          sb.append(\", \");\n        }\n      }\n      sb.append(\")\");\n      return sb.toString();\n    }\n  }\n\n  public static class Record extends AbstractConstructor<ConstructorSymbol> {\n\n    private Record(RecordSymbol sym, Term[] args) {\n      super(sym, args);\n    }\n\n    @Override\n    public void toSmtLib(SmtLibShim shim) {\n      Constructors.toSmtLib(this, shim);\n    }\n\n    @Override\n    public String toString() {\n      StringBuilder sb = new StringBuilder(\"{ \");\n      int i = 0;\n      for (FunctionSymbol label : ((RecordSymbol) sym).getLabels()) {\n        sb.append(label);\n        sb.append(\"=\");\n        sb.append(args[i]);\n        sb.append(\"; \");\n        ++i;\n      }\n      sb.append(\"}\");\n      return sb.toString();\n    }\n  }\n\n  public static class SolverVariable extends AbstractConstructor<ParameterizedConstructorSymbol> {\n\n    private static final AtomicInteger cnt = new AtomicInteger();\n\n    private final int solverVarId = cnt.getAndIncrement();\n\n    public SolverVariable(ParameterizedConstructorSymbol sym, Term[] args) {\n      super(sym, args);\n    }\n\n    @Override\n    public void toSmtLib(SmtLibShim shim) {\n      shim.print(this);\n    }\n\n    public int getSolverVarId() {\n      return solverVarId;\n    }\n\n    @Override\n    public String toString() {\n      if (Configuration.simplifyFormulaVars) {\n        return \"#x\" + getSolverVarId();\n      }\n      Type ty = ((FunctorType) sym.getCompileTimeType()).getRetType();\n      ty = ((AlgebraicDataType) ty).getTypeArgs().get(0);\n      return \"#{\" + args[0] + \"}[\" + ty + \"]\";\n    }\n\n    @Override\n    public Set<SolverVariable> freeVars() {\n      return Collections.singleton(this);\n    }\n  }\n\n  private static Constructor makeConstructorTester(ConstructorSymbol sym, Term[] args) {\n    assert sym.toString().matches(\"#is_.*\");\n    String s = \"|is-\" + sym.toString().substring(4) + \"|\";\n    return new AbstractConstructor<ConstructorSymbol>(sym, args) {\n\n      @Override\n      public void toSmtLib(SmtLibShim shim) {\n        shim.print(\"(\");\n        shim.print(s);\n        shim.print(\" \");\n        ((SmtLibTerm) args[0]).toSmtLib(shim);\n        shim.print(\")\");\n      }\n    };\n  }\n\n  private static Constructor makeConstructorGetter(ConstructorSymbol sym, Term[] args) {\n    String s = \"|\" + sym.toString() + \"|\";\n    return new AbstractConstructor<ConstructorSymbol>(sym, args) {\n\n      @Override\n      public void toSmtLib(SmtLibShim shim) {\n        shim.print(\"(\");\n        shim.print(s);\n        shim.print(\" \");\n        ((SmtLibTerm) args[0]).toSmtLib(shim);\n        shim.print(\")\");\n      }\n    };\n  }\n\n  public static class SolverUninterpretedFunction extends AbstractConstructor<ConstructorSymbol> {\n\n    protected SolverUninterpretedFunction(ConstructorSymbol sym, Term[] args) {\n      super(sym, args);\n    }\n\n    @Override\n    public void toSmtLib(SmtLibShim shim) {\n      Constructors.toSmtLib(this, shim);\n    }\n  }\n\n  public static class SolverOperation extends AbstractConstructor<ConstructorSymbol> {\n\n    private final String op;\n\n    protected SolverOperation(ConstructorSymbol sym, Term[] args, String op) {\n      super(sym, args);\n      this.op = op;\n    }\n\n    @Override\n    public void toSmtLib(SmtLibShim shim) {\n      Constructors.toSmtLib(this, op, shim);\n    }\n\n    private String getSyntax() {\n      if (sym instanceof ParameterizedConstructorSymbol) {\n        if (((ParameterizedConstructorSymbol) sym)\n            .getBase()\n            .equals(BuiltInConstructorSymbolBase.SMT_EQ)) {\n          return \"#=\";\n        }\n      }\n      if (sym instanceof BuiltInConstructorSymbol) {\n        switch ((BuiltInConstructorSymbol) sym) {\n          case SMT_AND:\n            return \"/\\\\\";\n          case SMT_IMP:\n            return \"==>\";\n          case SMT_NOT:\n            return \"~\";\n          case SMT_OR:\n            return \"\\\\/\";\n          default:\n            return null;\n        }\n      }\n      return null;\n    }\n\n    @Override\n    public String toString() {\n      String syntax = getSyntax();\n      if (syntax == null) {\n        return super.toString();\n      }\n      if (args.length == 1) {\n        return \"(\" + syntax + \" \" + args[0] + \")\";\n      }\n      if (args.length == 2) {\n        return \"(\" + args[0] + \" \" + syntax + \" \" + args[1] + \")\";\n      }\n      throw new AssertionError(\"impossible\");\n    }\n  }\n\n  private static class SolverIte extends SolverOperation {\n\n    protected SolverIte(ConstructorSymbol sym, Term[] args) {\n      super(sym, args, \"ite\");\n    }\n\n    @Override\n    public String toString() {\n      return \"(#if \" + args[0] + \" then \" + args[1] + \" else \" + args[2] + \")\";\n    }\n  }\n\n  private static void toSmtLib(Constructor c, String repr, SmtLibShim shim) {\n    ConstructorSymbol sym = c.getSymbol();\n    if (sym.getArity() > 0) {\n      shim.print(\"(\");\n    }\n    String typeAnnotation = shim.getTypeAnnotation(sym);\n    if (typeAnnotation != null) {\n      shim.print(\"(as \");\n      shim.print(repr);\n      shim.print(\" \");\n      shim.print(typeAnnotation);\n      shim.print(\")\");\n    } else {\n      shim.print(repr);\n    }\n    for (Term t : c.getArgs()) {\n      SmtLibTerm tt = (SmtLibTerm) t;\n      shim.print(\" \");\n      tt.toSmtLib(shim);\n    }\n    if (sym.getArity() > 0) {\n      shim.print(\")\");\n    }\n  }\n\n  private static void toSmtLib(Constructor c, SmtLibShim shim) {\n    toSmtLib(c, c.getSymbol().toString(), shim);\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/ast/Expr.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.ast;\n\nimport edu.harvard.seas.pl.formulog.ast.Exprs.ExprVisitor;\nimport edu.harvard.seas.pl.formulog.ast.Exprs.ExprVisitorExn;\nimport edu.harvard.seas.pl.formulog.ast.Terms.TermVisitor;\nimport edu.harvard.seas.pl.formulog.ast.Terms.TermVisitorExn;\n\npublic interface Expr extends Term {\n\n  <I, O> O accept(ExprVisitor<I, O> visitor, I in);\n\n  <I, O, E extends Throwable> O accept(ExprVisitorExn<I, O, E> visitor, I in) throws E;\n\n  @Override\n  default <I, O> O accept(TermVisitor<I, O> visitor, I in) {\n    return visitor.visit(this, in);\n  }\n\n  @Override\n  default <I, O, E extends Throwable> O accept(TermVisitorExn<I, O, E> visitor, I in) throws E {\n    return visitor.visit(this, in);\n  }\n\n  @Override\n  default boolean containsUnevaluatedTerm() {\n    return true;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/ast/Exprs.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.ast;\n\nimport edu.harvard.seas.pl.formulog.ast.FunctionCallFactory.FunctionCall;\n\npublic final class Exprs {\n\n  private Exprs() {\n    throw new AssertionError(\"impossible\");\n  }\n\n  public static interface ExprVisitor<I, O> {\n\n    O visit(MatchExpr matchExpr, I in);\n\n    O visit(FunctionCall funcCall, I in);\n\n    O visit(LetFunExpr funcDefs, I in);\n\n    O visit(Fold fold, I in);\n  }\n\n  public static interface ExprVisitorExn<I, O, E extends Throwable> {\n\n    O visit(MatchExpr matchExpr, I in) throws E;\n\n    O visit(FunctionCall funcCall, I in) throws E;\n\n    O visit(LetFunExpr funcDefs, I in) throws E;\n\n    O visit(Fold fold, I in) throws E;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/ast/FP32.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.ast;\n\nimport edu.harvard.seas.pl.formulog.ast.Constructors.SolverVariable;\nimport edu.harvard.seas.pl.formulog.smt.SmtLibShim;\nimport edu.harvard.seas.pl.formulog.types.BuiltInTypes;\nimport edu.harvard.seas.pl.formulog.types.Types.Type;\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport org.pcollections.PMap;\n\npublic class FP32 extends AbstractTerm implements Primitive<Float>, SmtLibTerm {\n\n  private static final Map<Float, FP32> memo = new ConcurrentHashMap<>();\n  private final float val;\n\n  private FP32(float val) {\n    this.val = val;\n  }\n\n  public static FP32 make(float val) {\n    return Util.lookupOrCreate(memo, val, () -> new FP32(val));\n  }\n\n  @Override\n  public Float getVal() {\n    return val;\n  }\n\n  @Override\n  public String toString() {\n    if (Float.isNaN(val)) {\n      return \"fp32_nan\";\n    }\n    if (val == Float.POSITIVE_INFINITY) {\n      return \"fp32_pos_infinity\";\n    }\n    if (val == Float.NEGATIVE_INFINITY) {\n      return \"fp32_neg_infinity\";\n    }\n    return Float.toString(val) + \"F\";\n  }\n\n  @Override\n  public SmtLibTerm substSolverTerms(PMap<SolverVariable, SmtLibTerm> subst) {\n    return this;\n  }\n\n  @Override\n  public void toSmtLib(SmtLibShim shim) {\n    if (Float.isNaN(val)) {\n      shim.print(\"(_ NaN 8 24)\");\n    } else if (val == Float.POSITIVE_INFINITY) {\n      shim.print(\"(_ +oo 8 24)\");\n    } else if (val == Float.NEGATIVE_INFINITY) {\n      shim.print(\"(_ -oo 8 24)\");\n    } else {\n      shim.print(\"((_ to_fp 8 24) RNE \" + val + \")\");\n    }\n  }\n\n  @Override\n  public Type getType() {\n    return BuiltInTypes.fp32;\n  }\n\n  @Override\n  public Set<SolverVariable> freeVars() {\n    return Collections.emptySet();\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/ast/FP64.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.ast;\n\nimport edu.harvard.seas.pl.formulog.ast.Constructors.SolverVariable;\nimport edu.harvard.seas.pl.formulog.smt.SmtLibShim;\nimport edu.harvard.seas.pl.formulog.types.BuiltInTypes;\nimport edu.harvard.seas.pl.formulog.types.Types.Type;\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport org.pcollections.PMap;\n\npublic class FP64 extends AbstractTerm implements Primitive<Double>, SmtLibTerm {\n\n  private static final Map<Double, FP64> memo = new ConcurrentHashMap<>();\n  private final double val;\n\n  private FP64(double val) {\n    this.val = val;\n  }\n\n  public static FP64 make(double val) {\n    return Util.lookupOrCreate(memo, val, () -> new FP64(val));\n  }\n\n  @Override\n  public Double getVal() {\n    return val;\n  }\n\n  @Override\n  public String toString() {\n    if (Double.isNaN(val)) {\n      return \"fp64_nan\";\n    }\n    if (val == Double.POSITIVE_INFINITY) {\n      return \"fp64_pos_infinity\";\n    }\n    if (val == Double.NEGATIVE_INFINITY) {\n      return \"fp64_neg_infinity\";\n    }\n    return Double.toString(val);\n  }\n\n  @Override\n  public SmtLibTerm substSolverTerms(PMap<SolverVariable, SmtLibTerm> subst) {\n    return this;\n  }\n\n  @Override\n  public void toSmtLib(SmtLibShim shim) {\n    if (Double.isNaN(val)) {\n      shim.print(\"(_ NaN 11 53)\");\n    } else if (val == Double.POSITIVE_INFINITY) {\n      shim.print(\"(_ +oo 11 53)\");\n    } else if (val == Double.NEGATIVE_INFINITY) {\n      shim.print(\"(_ -oo 11 53)\");\n    } else {\n      shim.print(\"((_ to_fp 11 53) RNE \" + val + \")\");\n    }\n  }\n\n  @Override\n  public Type getType() {\n    return BuiltInTypes.fp64;\n  }\n\n  @Override\n  public Set<SolverVariable> freeVars() {\n    return Collections.emptySet();\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/ast/Fold.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.ast;\n\nimport edu.harvard.seas.pl.formulog.ast.Exprs.ExprVisitor;\nimport edu.harvard.seas.pl.formulog.ast.Exprs.ExprVisitorExn;\nimport edu.harvard.seas.pl.formulog.ast.FunctionCallFactory.FunctionCall;\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport edu.harvard.seas.pl.formulog.functions.FunctionDef;\nimport edu.harvard.seas.pl.formulog.symbols.BuiltInConstructorSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.FunctionSymbol;\nimport edu.harvard.seas.pl.formulog.unification.Substitution;\nimport java.util.Arrays;\nimport java.util.Map;\nimport java.util.Set;\n\npublic class Fold implements Expr {\n\n  private final FunctionSymbol f;\n  private final Term[] args;\n  private final FunctionCallFactory funCalls;\n  private Boolean isGround;\n\n  private Fold(FunctionSymbol f, Term[] args, FunctionCallFactory funCalls) {\n    this.f = f;\n    this.args = args;\n    this.funCalls = funCalls;\n    if (f.getArity() != args.length) {\n      throw new IllegalArgumentException(\n          \"Arity mismatch when defining fold over \"\n              + f\n              + \" (it needs \"\n              + f.getArity()\n              + \" arguments, but got the arguments \"\n              + Arrays.toString(args)\n              + \").\");\n    }\n  }\n\n  public static Fold mk(FunctionSymbol f, Term[] args, FunctionCallFactory funCalls) {\n    return new Fold(f, args, funCalls);\n  }\n\n  public Term[] getArgs() {\n    return args;\n  }\n\n  public FunctionSymbol getFunction() {\n    return f;\n  }\n\n  public FunctionCall getShamCall() {\n    return funCalls.make(f, args);\n  }\n\n  @Override\n  public boolean isGround() {\n    if (isGround != null) {\n      return isGround;\n    }\n    for (Term arg : args) {\n      if (!arg.isGround()) {\n        return (isGround = false);\n      }\n    }\n    return (isGround = true);\n  }\n\n  @Override\n  public Term applySubstitution(Substitution s) {\n    Term[] newArgs = new Term[args.length];\n    int i = 0;\n    for (Term arg : args) {\n      newArgs[i] = arg.applySubstitution(s);\n      i++;\n    }\n    return new Fold(f, newArgs, funCalls);\n  }\n\n  @Override\n  public Term normalize(Substitution s) throws EvaluationException {\n    Term[] newArgs = new Term[args.length];\n    for (int i = 0; i < args.length; ++i) {\n      newArgs[i] = args[i].normalize(s);\n    }\n    Term acc = newArgs[0];\n    Constructor list = (Constructor) newArgs[1];\n    FunctionDef def = funCalls.getDefManager().lookup(f);\n    while (list.getSymbol().equals(BuiltInConstructorSymbol.CONS)) {\n      Term[] consCell = list.getArgs();\n      Term head = consCell[0];\n      list = (Constructor) consCell[1];\n      newArgs[0] = acc;\n      newArgs[1] = head;\n      acc = def.evaluate(newArgs);\n    }\n    return acc;\n  }\n\n  @Override\n  public void varSet(Set<Var> acc) {\n    for (Term arg : args) {\n      arg.varSet(acc);\n    }\n  }\n\n  @Override\n  public void updateVarCounts(Map<Var, Integer> counts) {\n    for (Term arg : args) {\n      arg.updateVarCounts(counts);\n    }\n  }\n\n  @Override\n  public int getId() {\n    throw new UnsupportedOperationException();\n  }\n\n  @Override\n  public <I, O> O accept(ExprVisitor<I, O> visitor, I in) {\n    return visitor.visit(this, in);\n  }\n\n  @Override\n  public <I, O, E extends Throwable> O accept(ExprVisitorExn<I, O, E> visitor, I in) throws E {\n    return visitor.visit(this, in);\n  }\n\n  @Override\n  public String toString() {\n    String s = \"fold[\" + f + \"](\";\n    for (int i = 0; i < args.length; ++i) {\n      s += args[i];\n      if (i < args.length - 1) {\n        s += \", \";\n      }\n    }\n    return s + \")\";\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/ast/FunctionCallFactory.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.ast;\n\nimport edu.harvard.seas.pl.formulog.Configuration;\nimport edu.harvard.seas.pl.formulog.ast.Exprs.ExprVisitor;\nimport edu.harvard.seas.pl.formulog.ast.Exprs.ExprVisitorExn;\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport edu.harvard.seas.pl.formulog.functions.FunctionDefManager;\nimport edu.harvard.seas.pl.formulog.symbols.BuiltInFunctionSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.FunctionSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.Symbol;\nimport edu.harvard.seas.pl.formulog.unification.Substitution;\nimport edu.harvard.seas.pl.formulog.util.FunctorUtil;\nimport edu.harvard.seas.pl.formulog.util.FunctorUtil.Memoizer;\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.atomic.AtomicInteger;\n\npublic final class FunctionCallFactory {\n\n  private final FunctionDefManager defManager;\n  private final Memoizer<FunctionCall> memo = new Memoizer<>();\n\n  private static final AtomicInteger cnt = new AtomicInteger();\n  private static final boolean debug = System.getProperty(\"callTrace\") != null;\n  private static final int memoizeThreshold = Configuration.memoizeThreshold();\n\n  private final Map<Symbol, Map<List<Term>, Term>> callMemo = new ConcurrentHashMap<>();\n\n  public FunctionCallFactory(FunctionDefManager defManager) {\n    this.defManager = defManager;\n  }\n\n  public FunctionCall make(FunctionSymbol sym, Term[] args) {\n    if (sym.getArity() != args.length) {\n      throw new IllegalArgumentException(\n          \"Symbol \"\n              + sym\n              + \" has arity \"\n              + sym.getArity()\n              + \" but args \"\n              + Arrays.toString(args)\n              + \" have length \"\n              + args.length);\n    }\n    return memo.lookupOrCreate(sym, args, () -> new FunctionCall(sym, args));\n  }\n\n  public FunctionDefManager getDefManager() {\n    return defManager;\n  }\n\n  public void clearMemoCache() {\n    callMemo.clear();\n  }\n\n  public class FunctionCall extends AbstractTerm implements Functor<FunctionSymbol>, Expr {\n\n    private final FunctionSymbol sym;\n    private final Term[] args;\n    private final boolean isGround;\n\n    private FunctionCall(FunctionSymbol sym, Term[] args) {\n      this.sym = sym;\n      this.args = args;\n      boolean b = true;\n      for (Term t : args) {\n        b &= t.isGround();\n      }\n      isGround = b;\n    }\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return sym;\n    }\n\n    @Override\n    public Term[] getArgs() {\n      return args;\n    }\n\n    @Override\n    public FunctionCall copyWithNewArgs(Term[] newArgs) {\n      return make(sym, newArgs);\n    }\n\n    @Override\n    public boolean containsUnevaluatedTerm() {\n      return true;\n    }\n\n    @Override\n    public Term applySubstitution(Substitution s) {\n      if (isGround) {\n        return this;\n      }\n      Term[] args = getArgs();\n      Term[] newArgs = new Term[args.length];\n      for (int i = 0; i < args.length; ++i) {\n        newArgs[i] = args[i].applySubstitution(s);\n      }\n      return make(sym, newArgs);\n    }\n\n    @Override\n    public boolean isGround() {\n      return isGround;\n    }\n\n    @Override\n    public String toString() {\n      return FunctorUtil.toString(sym, args);\n    }\n\n    @Override\n    public Term normalize(Substitution s) throws EvaluationException {\n      Integer id = null;\n      Term[] newArgs = new Term[args.length];\n      for (int i = 0; i < args.length; ++i) {\n        newArgs[i] = args[i].normalize(s);\n      }\n      if (debug) {\n        id = cnt.getAndIncrement();\n        String msg = \"BEGIN CALL #\" + id + \"\\n\";\n        msg += \"Function: \" + sym + \"\\n\";\n        msg += \"Arguments: \" + Arrays.toString(newArgs) + \"\\n\";\n        msg += \"***\\n\";\n        System.err.println(msg);\n      }\n      Term r;\n      try {\n        if (memoizeThreshold > -1 && !hasSideEffects()) {\n          r = computeWithMemoization(newArgs);\n        } else {\n          r = computeWithoutMemoization(newArgs);\n        }\n      } catch (Throwable e) {\n        throw new EvaluationException(e);\n      }\n      if (debug) {\n        String msg = \"END CALL #\" + id + \"\\n\";\n        msg += \"Function: \" + sym + \"\\n\";\n        msg += \"Arguments: \" + Arrays.toString(newArgs) + \"\\n\";\n        msg += \"Result: \" + r + \"\\n\";\n        msg += \"***\\n\";\n        System.err.println(msg);\n      }\n      return r;\n    }\n\n    private boolean hasSideEffects() {\n      // XXX This is rough, since it doesn't take into account the call\n      // graph (i.e., A could call B which is side effecting, but this\n      // would say that A is not).\n      return sym.equals(BuiltInFunctionSymbol.PRINT);\n    }\n\n    private Term computeWithMemoization(Term[] newArgs) throws EvaluationException {\n      Map<List<Term>, Term> m = Util.lookupOrCreate(callMemo, sym, () -> new ConcurrentHashMap<>());\n      List<Term> key = Arrays.asList(newArgs);\n      Term r = m.get(key);\n      if (r == null) {\n        long start = System.nanoTime();\n        r = defManager.lookup(sym).evaluate(newArgs);\n        long time = (System.nanoTime() - start) / 1000000;\n        if (Configuration.recordFuncDiagnostics) {\n          Configuration.recordFuncTime(sym, time);\n        }\n        if (time >= memoizeThreshold) {\n          m.put(key, r);\n        }\n      }\n      return r;\n    }\n\n    private Term computeWithoutMemoization(Term[] newArgs) throws EvaluationException {\n      long start = 0;\n      if (Configuration.recordFuncDiagnostics) {\n        start = System.nanoTime();\n      }\n      Term r = defManager.lookup(sym).evaluate(newArgs);\n      if (Configuration.recordFuncDiagnostics) {\n        long time = (System.nanoTime() - start) / 1000000;\n        Configuration.recordFuncTime(sym, time);\n      }\n      return r;\n    }\n\n    public FunctionCallFactory getFactory() {\n      return FunctionCallFactory.this;\n    }\n\n    @Override\n    public <I, O> O accept(ExprVisitor<I, O> visitor, I in) {\n      return visitor.visit(this, in);\n    }\n\n    @Override\n    public <I, O, E extends Throwable> O accept(ExprVisitorExn<I, O, E> visitor, I in) throws E {\n      return visitor.visit(this, in);\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/ast/Functor.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.ast;\n\nimport edu.harvard.seas.pl.formulog.symbols.Symbol;\nimport java.util.Map;\nimport java.util.Set;\n\npublic interface Functor<S extends Symbol> extends Term {\n\n  S getSymbol();\n\n  Term[] getArgs();\n\n  Term copyWithNewArgs(Term[] args);\n\n  @Override\n  default void varSet(Set<Var> acc) {\n    if (!isGround()) {\n      for (Term arg : getArgs()) {\n        arg.varSet(acc);\n      }\n    }\n  }\n\n  @Override\n  default void updateVarCounts(Map<Var, Integer> counts) {\n    for (Term arg : getArgs()) {\n      arg.updateVarCounts(counts);\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/ast/I32.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.ast;\n\nimport edu.harvard.seas.pl.formulog.ast.Constructors.SolverVariable;\nimport edu.harvard.seas.pl.formulog.smt.SmtLibShim;\nimport edu.harvard.seas.pl.formulog.types.BuiltInTypes;\nimport edu.harvard.seas.pl.formulog.types.Types.Type;\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport org.pcollections.PMap;\n\npublic class I32 extends AbstractTerm implements Primitive<Integer>, SmtLibTerm {\n\n  private static final Map<Integer, I32> memo = new ConcurrentHashMap<>();\n  private final int val;\n\n  private I32(int val) {\n    this.val = val;\n  }\n\n  public static I32 make(int val) {\n    return Util.lookupOrCreate(memo, val, () -> new I32(val));\n  }\n\n  @Override\n  public Integer getVal() {\n    return val;\n  }\n\n  @Override\n  public String toString() {\n    return Integer.toString(val);\n  }\n\n  @Override\n  public SmtLibTerm substSolverTerms(PMap<SolverVariable, SmtLibTerm> subst) {\n    return this;\n  }\n\n  @Override\n  public void toSmtLib(SmtLibShim shim) {\n    shim.print(\"#x\" + String.format(\"%08x\", val));\n  }\n\n  @Override\n  public Type getType() {\n    return BuiltInTypes.i32;\n  }\n\n  @Override\n  public Set<SolverVariable> freeVars() {\n    return Collections.emptySet();\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/ast/I64.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.ast;\n\nimport edu.harvard.seas.pl.formulog.ast.Constructors.SolverVariable;\nimport edu.harvard.seas.pl.formulog.smt.SmtLibShim;\nimport edu.harvard.seas.pl.formulog.types.BuiltInTypes;\nimport edu.harvard.seas.pl.formulog.types.Types.Type;\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport org.pcollections.PMap;\n\npublic class I64 extends AbstractTerm implements Primitive<Long>, SmtLibTerm {\n\n  private static final Map<Long, I64> memo = new ConcurrentHashMap<>();\n  private final long val;\n\n  private I64(long val) {\n    this.val = val;\n  }\n\n  public static I64 make(long val) {\n    return Util.lookupOrCreate(memo, val, () -> new I64(val));\n  }\n\n  @Override\n  public Long getVal() {\n    return val;\n  }\n\n  @Override\n  public String toString() {\n    return Long.toString(val) + \"L\";\n  }\n\n  @Override\n  public SmtLibTerm substSolverTerms(PMap<SolverVariable, SmtLibTerm> subst) {\n    return this;\n  }\n\n  @Override\n  public void toSmtLib(SmtLibShim shim) {\n    shim.print(\"#x\" + String.format(\"%016x\", val));\n  }\n\n  @Override\n  public Type getType() {\n    return BuiltInTypes.i64;\n  }\n\n  @Override\n  public Set<SolverVariable> freeVars() {\n    return Collections.emptySet();\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/ast/LetFunExpr.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.ast;\n\nimport edu.harvard.seas.pl.formulog.ast.Exprs.ExprVisitor;\nimport edu.harvard.seas.pl.formulog.ast.Exprs.ExprVisitorExn;\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport edu.harvard.seas.pl.formulog.unification.Substitution;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.Set;\n\npublic class LetFunExpr implements Expr {\n\n  private final Set<NestedFunctionDef> defs;\n  private final Term letBody;\n\n  private LetFunExpr(Set<NestedFunctionDef> defs, Term letBody) {\n    Set<NestedFunctionDef> freshDefs = new HashSet<>();\n    for (NestedFunctionDef def : defs) {\n      freshDefs.add(def.freshen());\n    }\n    this.defs = Collections.unmodifiableSet(freshDefs);\n    this.letBody = letBody;\n  }\n\n  public static LetFunExpr make(Set<NestedFunctionDef> defs, Term letBody) {\n    return new LetFunExpr(defs, letBody);\n  }\n\n  public Set<NestedFunctionDef> getDefs() {\n    return defs;\n  }\n\n  public Term getLetBody() {\n    return letBody;\n  }\n\n  @Override\n  public boolean isGround() {\n    for (NestedFunctionDef def : defs) {\n      if (!def.getBody().isGround()) {\n        return false;\n      }\n    }\n    return letBody.isGround();\n  }\n\n  @Override\n  public Term applySubstitution(Substitution s) {\n    Set<NestedFunctionDef> newDefs = new HashSet<>();\n    for (NestedFunctionDef def : defs) {\n      newDefs.add(def.applySubstitution(s));\n    }\n    return make(newDefs, letBody.applySubstitution(s));\n  }\n\n  @Override\n  public Term normalize(Substitution s) throws EvaluationException {\n    throw new UnsupportedOperationException();\n  }\n\n  @Override\n  public void varSet(Set<Var> acc) {\n    for (NestedFunctionDef def : defs) {\n      Set<Var> vars = def.getBody().varSet();\n      vars.removeAll(def.getParams());\n      acc.addAll(vars);\n    }\n  }\n\n  @Override\n  public void updateVarCounts(Map<Var, Integer> counts) {\n    throw new UnsupportedOperationException(\"LetFunExpr.updateVarCounts() unimplemented\");\n  }\n\n  @Override\n  public int getId() {\n    throw new UnsupportedOperationException();\n  }\n\n  @Override\n  public <I, O> O accept(ExprVisitor<I, O> visitor, I in) {\n    return visitor.visit(this, in);\n  }\n\n  @Override\n  public <I, O, E extends Throwable> O accept(ExprVisitorExn<I, O, E> visitor, I in) throws E {\n    return visitor.visit(this, in);\n  }\n\n  @Override\n  public int hashCode() {\n    final int prime = 31;\n    int result = 1;\n    result = prime * result + ((defs == null) ? 0 : defs.hashCode());\n    result = prime * result + ((letBody == null) ? 0 : letBody.hashCode());\n    return result;\n  }\n\n  @Override\n  public boolean equals(Object obj) {\n    if (this == obj) return true;\n    if (obj == null) return false;\n    if (getClass() != obj.getClass()) return false;\n    LetFunExpr other = (LetFunExpr) obj;\n    if (defs == null) {\n      if (other.defs != null) return false;\n    } else if (!defs.equals(other.defs)) return false;\n    if (letBody == null) {\n      if (other.letBody != null) return false;\n    } else if (!letBody.equals(other.letBody)) return false;\n    return true;\n  }\n\n  @Override\n  public String toString() {\n    String s = \"let fun \";\n    for (Iterator<NestedFunctionDef> it = defs.iterator(); it.hasNext(); ) {\n      s += it.next();\n      if (it.hasNext()) {\n        s += \"\\nand \";\n      }\n    }\n    return s + \" in\\n\" + letBody;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/ast/Literal.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.ast;\n\npublic interface Literal {\n\n  Term[] getArgs();\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/ast/MatchClause.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.ast;\n\nimport edu.harvard.seas.pl.formulog.ast.Terms.TermVisitor;\nimport edu.harvard.seas.pl.formulog.unification.SimpleSubstitution;\nimport edu.harvard.seas.pl.formulog.unification.Substitution;\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport java.util.ArrayDeque;\nimport java.util.Deque;\nimport java.util.HashSet;\nimport java.util.Set;\n\npublic class MatchClause {\n\n  private final Term lhs;\n  private final Term rhs;\n\n  public static MatchClause make(Term lhs, Term rhs) {\n    if (!checkForRepeatVariables(lhs)) {\n      throw new IllegalArgumentException(\"Cannot repeat variables in patterns: \" + lhs);\n    }\n    Substitution s = new SimpleSubstitution();\n    for (Var x : lhs.varSet()) {\n      if (!x.isUnderscore()) {\n        s.put(x, Var.fresh(x.toString()));\n      }\n    }\n    return new MatchClause(lhs.applySubstitution(s), rhs.applySubstitution(s));\n  }\n\n  private static boolean checkForRepeatVariables(Term pat) {\n    return pat.accept(varsDistinct, new HashSet<>());\n  }\n\n  private static TermVisitor<Set<String>, Boolean> varsDistinct =\n      new TermVisitor<Set<String>, Boolean>() {\n\n        @Override\n        public Boolean visit(Var x, Set<String> in) {\n          if (x.isUnderscore()) {\n            return true;\n          }\n          return in.add(x.getName());\n        }\n\n        @Override\n        public Boolean visit(Constructor c, Set<String> in) {\n          boolean ok = true;\n          for (Term t : c.getArgs()) {\n            ok &= t.accept(this, in);\n          }\n          return ok;\n        }\n\n        @Override\n        public Boolean visit(Primitive<?> p, Set<String> in) {\n          return true;\n        }\n\n        @Override\n        public Boolean visit(Expr e, Set<String> in) {\n          throw new AssertionError(\"Shouldn't be expressions in patterns\");\n        }\n      };\n\n  MatchClause(Term lhs, Term rhs) {\n    this.lhs = lhs;\n    this.rhs = rhs;\n  }\n\n  public boolean tryMatch(Term t, Substitution s) {\n    Deque<Pair<Term, Term>> stack = new ArrayDeque<>();\n    stack.add(new Pair<>(lhs, t));\n    while (!stack.isEmpty()) {\n      Pair<Term, Term> p = stack.remove();\n      Term pat = p.fst();\n      Term scrutinee = p.snd();\n      if (pat.equals(scrutinee)) {\n        continue;\n      }\n      if (pat instanceof Var) {\n        s.put((Var) pat, scrutinee);\n      } else if (pat instanceof Primitive) {\n        return false;\n      } else {\n        Constructor cpat = (Constructor) pat;\n        Constructor cscrutinee = (Constructor) scrutinee;\n        if (!cpat.getSymbol().equals(cscrutinee.getSymbol())) {\n          return false;\n        }\n        Term[] args1 = cpat.getArgs();\n        Term[] args2 = cscrutinee.getArgs();\n        for (int i = 0; i < args1.length; ++i) {\n          stack.add(new Pair<>(args1[i], args2[i]));\n        }\n      }\n    }\n    return true;\n  }\n\n  public Term getLhs() {\n    return lhs;\n  }\n\n  public Term getRhs() {\n    return rhs;\n  }\n\n  @Override\n  public int hashCode() {\n    final int prime = 31;\n    int result = 1;\n    result = prime * result + ((lhs == null) ? 0 : lhs.hashCode());\n    result = prime * result + ((rhs == null) ? 0 : rhs.hashCode());\n    return result;\n  }\n\n  @Override\n  public boolean equals(Object obj) {\n    if (this == obj) return true;\n    if (obj == null) return false;\n    if (getClass() != obj.getClass()) return false;\n    MatchClause other = (MatchClause) obj;\n    if (lhs == null) {\n      if (other.lhs != null) return false;\n    } else if (!lhs.equals(other.lhs)) return false;\n    if (rhs == null) {\n      if (other.rhs != null) return false;\n    } else if (!rhs.equals(other.rhs)) return false;\n    return true;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/ast/MatchExpr.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.ast;\n\nimport edu.harvard.seas.pl.formulog.ast.Exprs.ExprVisitor;\nimport edu.harvard.seas.pl.formulog.ast.Exprs.ExprVisitorExn;\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport edu.harvard.seas.pl.formulog.unification.SimpleSubstitution;\nimport edu.harvard.seas.pl.formulog.unification.Substitution;\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\npublic class MatchExpr extends AbstractTerm implements Expr, Iterable<MatchClause> {\n\n  private final Term matchee;\n  private final List<MatchClause> match;\n  private final boolean isGround;\n\n  public static MatchExpr make(Term matchee, List<MatchClause> match) {\n    return new MatchExpr(matchee, match);\n  }\n\n  MatchExpr(Term matchee, List<MatchClause> match) {\n    this.matchee = matchee;\n    this.match = match;\n    boolean isGround = matchee.isGround();\n    if (isGround) {\n      for (MatchClause cl : match) {\n        Set<Var> vars = cl.getRhs().varSet();\n        Set<Var> patVars = cl.getLhs().varSet();\n        if (!patVars.containsAll(vars)) {\n          isGround = false;\n          break;\n        }\n      }\n    }\n    this.isGround = isGround;\n  }\n\n  public Term getMatchee() {\n    return matchee;\n  }\n\n  public List<MatchClause> getClauses() {\n    return Collections.unmodifiableList(match);\n  }\n\n  @Override\n  public <I, O, E extends Throwable> O accept(ExprVisitorExn<I, O, E> visitor, I in) throws E {\n    return visitor.visit(this, in);\n  }\n\n  @Override\n  public Term normalize(Substitution s) throws EvaluationException {\n    Term e = matchee.normalize(s);\n    for (MatchClause m : match) {\n      if (m.tryMatch(e, s)) {\n        return m.getRhs().normalize(s);\n      }\n    }\n    if (e instanceof Constructor) {\n      throw new EvaluationException(\n          \"No matching pattern for \"\n              + matchee\n              + \" which normalizes to a complex term with outermost constructor \"\n              + ((Constructor) e).getSymbol());\n    } else {\n      throw new EvaluationException(\n          \"No matching pattern for \" + matchee + \" which normalizes to the term \" + e);\n    }\n    // throw new EvaluationException(\"No matching pattern for \" + e + \" under\n    // substitution \" + s);\n  }\n\n  @Override\n  public <I, O> O accept(ExprVisitor<I, O> visitor, I in) {\n    return visitor.visit(this, in);\n  }\n\n  @Override\n  public Term applySubstitution(Substitution s) {\n    if (isGround) {\n      return this;\n    }\n    Term newMatchee = matchee.applySubstitution(s);\n    List<MatchClause> clauses = new ArrayList<>();\n    for (MatchClause cl : match) {\n      Substitution newS = new SimpleSubstitution();\n      Term pat = cl.getLhs();\n      Set<Var> patVars = pat.varSet();\n      for (Var x : s.iterateKeys()) {\n        if (!patVars.contains(x)) {\n          newS.put(x, s.get(x));\n        }\n      }\n      Term newRhs = cl.getRhs().applySubstitution(newS);\n      clauses.add(new MatchClause(pat, newRhs));\n    }\n    return make(newMatchee, clauses);\n  }\n\n  @Override\n  public String toString() {\n    String s = \"match \" + matchee.toString() + \" with\";\n    for (MatchClause cl : match) {\n      s += \"\\n\\t| \" + cl.getLhs() + \" => \" + cl.getRhs();\n    }\n    s += \"\\nend\";\n    return s;\n  }\n\n  @Override\n  public int hashCode() {\n    final int prime = 31;\n    int result = 1;\n    result = prime * result + ((match == null) ? 0 : match.hashCode());\n    result = prime * result + ((matchee == null) ? 0 : matchee.hashCode());\n    return result;\n  }\n\n  @Override\n  public boolean equals(Object obj) {\n    if (this == obj) return true;\n    if (obj == null) return false;\n    if (getClass() != obj.getClass()) return false;\n    MatchExpr other = (MatchExpr) obj;\n    if (match == null) {\n      if (other.match != null) return false;\n    } else if (!match.equals(other.match)) return false;\n    if (matchee == null) {\n      if (other.matchee != null) return false;\n    } else if (!matchee.equals(other.matchee)) return false;\n    return true;\n  }\n\n  @Override\n  public boolean isGround() {\n    return isGround;\n  }\n\n  @Override\n  public void varSet(Set<Var> acc) {\n    if (!isGround) {\n      matchee.varSet(acc);\n      for (MatchClause cl : match) {\n        Set<Var> vars = cl.getRhs().varSet();\n        vars.removeAll(cl.getLhs().varSet());\n        acc.addAll(vars);\n      }\n    }\n  }\n\n  @Override\n  public Iterator<MatchClause> iterator() {\n    return match.iterator();\n  }\n\n  @Override\n  public void updateVarCounts(Map<Var, Integer> counts) {\n    matchee.updateVarCounts(counts);\n    for (MatchClause match : this) {\n      Map<Var, Integer> counts2 = new HashMap<>();\n      match.getRhs().updateVarCounts(counts2);\n      Set<Var> boundVars = match.getLhs().varSet();\n      for (Map.Entry<Var, Integer> e : counts2.entrySet()) {\n        Var x = e.getKey();\n        if (!boundVars.contains(x)) {\n          int n = Util.lookupOrCreate(counts, x, () -> 0);\n          counts.put(x, n + e.getValue());\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/ast/Model.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.ast;\n\nimport edu.harvard.seas.pl.formulog.ast.Constructors.SolverVariable;\nimport edu.harvard.seas.pl.formulog.types.BuiltInTypes;\nimport edu.harvard.seas.pl.formulog.types.Types.Type;\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\npublic class Model extends AbstractTerm implements Primitive<Map<SolverVariable, Term>> {\n\n  private static final Map<Map<SolverVariable, Term>, Model> memo = new ConcurrentHashMap<>();\n\n  private final Map<SolverVariable, Term> m;\n\n  private Model(Map<SolverVariable, Term> m) {\n    this.m = m;\n  }\n\n  public static Model make(Map<SolverVariable, Term> m) {\n    return Util.lookupOrCreate(memo, m, () -> new Model(m));\n  }\n\n  @Override\n  public Map<SolverVariable, Term> getVal() {\n    return m;\n  }\n\n  @Override\n  public Type getType() {\n    return BuiltInTypes.model;\n  }\n\n  @Override\n  public String toString() {\n    String s = \"[\";\n    for (Iterator<Map.Entry<SolverVariable, Term>> it = m.entrySet().iterator(); it.hasNext(); ) {\n      Map.Entry<SolverVariable, Term> e = it.next();\n      s += e.getKey() + \" -> \" + e.getValue();\n      if (it.hasNext()) {\n        s += \", \";\n      }\n    }\n    return s + \"]\";\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/ast/NestedFunctionDef.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.ast;\n\nimport edu.harvard.seas.pl.formulog.symbols.FunctionSymbol;\nimport edu.harvard.seas.pl.formulog.unification.SimpleSubstitution;\nimport edu.harvard.seas.pl.formulog.unification.Substitution;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\npublic class NestedFunctionDef {\n\n  private final FunctionSymbol sym;\n  private final List<Var> params;\n  private final Term body;\n\n  private NestedFunctionDef(FunctionSymbol sym, List<Var> params, Term body) {\n    this.sym = sym;\n    this.params = Collections.unmodifiableList(params);\n    this.body = body;\n    if (sym.getArity() != params.size()) {\n      throw new IllegalArgumentException(\n          \"Mismatch between arity and number of given parameters in nested function definition: \"\n              + sym);\n    }\n  }\n\n  public static NestedFunctionDef make(FunctionSymbol sym, List<Var> params, Term body) {\n    return new NestedFunctionDef(sym, params, body);\n  }\n\n  public NestedFunctionDef applySubstitution(Substitution s) {\n    Substitution s2 = new SimpleSubstitution();\n    for (Var x : s.iterateKeys()) {\n      if (!params.contains(x)) {\n        s2.put(x, s.get(x));\n      }\n    }\n    return make(sym, params, body.applySubstitution(s2));\n  }\n\n  public NestedFunctionDef freshen() {\n    List<Var> newParams = new ArrayList<>();\n    Substitution s = new SimpleSubstitution();\n    for (Var param : params) {\n      Var newParam = Var.fresh(param.toString());\n      newParams.add(newParam);\n      s.put(param, newParam);\n    }\n    return make(sym, newParams, body.applySubstitution(s));\n  }\n\n  public FunctionSymbol getSymbol() {\n    return sym;\n  }\n\n  public List<Var> getParams() {\n    return params;\n  }\n\n  public Term getBody() {\n    return body;\n  }\n\n  @Override\n  public int hashCode() {\n    final int prime = 31;\n    int result = 1;\n    result = prime * result + ((params == null) ? 0 : params.hashCode());\n    result = prime * result + ((body == null) ? 0 : body.hashCode());\n    return result;\n  }\n\n  @Override\n  public boolean equals(Object obj) {\n    if (this == obj) return true;\n    if (obj == null) return false;\n    if (getClass() != obj.getClass()) return false;\n    NestedFunctionDef other = (NestedFunctionDef) obj;\n    if (params == null) {\n      if (other.params != null) return false;\n    } else if (!params.equals(other.params)) return false;\n    if (body == null) {\n      if (other.body != null) return false;\n    } else if (!body.equals(other.body)) return false;\n    return true;\n  }\n\n  @Override\n  public String toString() {\n    String s = \"\" + sym;\n    if (!params.isEmpty()) {\n      s += \"(\";\n      for (int i = 0; i < params.size(); ++i) {\n        s += params.get(i);\n        if (i < params.size() - 1) {\n          s += \", \";\n        }\n      }\n      s += \")\";\n    }\n    return s + \" = \" + body;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/ast/OpaqueSet.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2021-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.ast;\n\nimport edu.harvard.seas.pl.formulog.types.BuiltInTypes;\nimport edu.harvard.seas.pl.formulog.types.Types.Type;\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport org.pcollections.HashPMap;\nimport org.pcollections.IntTreePMap;\nimport org.pcollections.MapPSet;\nimport org.pcollections.PSet;\n\npublic class OpaqueSet extends AbstractTerm implements Primitive<Set<Term>> {\n\n  private final PSet<Term> s;\n\n  private OpaqueSet(PSet<Term> s) {\n    this.s = s;\n  }\n\n  private static final OpaqueSet empty;\n\n  private static final Map<PSet<Term>, OpaqueSet> memo = new ConcurrentHashMap<>();\n\n  static {\n    PSet<Term> mt = MapPSet.from(HashPMap.empty(IntTreePMap.empty()));\n    empty = make(mt);\n  }\n\n  public static OpaqueSet empty() {\n    return empty;\n  }\n\n  public static OpaqueSet singleton(Term t) {\n    return empty.plus(t);\n  }\n\n  public static OpaqueSet fromCollection(Collection<Term> c) {\n    PSet<Term> s = MapPSet.from(HashPMap.empty(IntTreePMap.empty()));\n    for (Term t : c) {\n      s = s.plus(t);\n    }\n    return make(s);\n  }\n\n  private static OpaqueSet make(PSet<Term> s) {\n    return Util.lookupOrCreate(memo, s, () -> new OpaqueSet(s));\n  }\n\n  public Collection<Term> getCollection() {\n    return s;\n  }\n\n  public boolean member(Term t) {\n    return s.contains(t);\n  }\n\n  public OpaqueSet plus(Term t) {\n    return make(s.plus(t));\n  }\n\n  public OpaqueSet minus(Term t) {\n    return make(s.minus(t));\n  }\n\n  public OpaqueSet union(OpaqueSet other) {\n    return make(s.plusAll(other.s));\n  }\n\n  public OpaqueSet diff(OpaqueSet other) {\n    return make(s.minusAll(other.s));\n  }\n\n  public int size() {\n    return s.size();\n  }\n\n  public boolean isEmpty() {\n    return s.isEmpty();\n  }\n\n  public Pair<Term, OpaqueSet> choose() {\n    if (isEmpty()) {\n      return null;\n    }\n    Term t = s.iterator().next();\n    return new Pair<>(t, make(s.minus(t)));\n  }\n\n  public boolean containsAll(OpaqueSet other) {\n    return s.containsAll(other.s);\n  }\n\n  @Override\n  public Set<Term> getVal() {\n    return s;\n  }\n\n  @Override\n  public Type getType() {\n    return BuiltInTypes.opaqueSet(BuiltInTypes.a);\n  }\n\n  @Override\n  public String toString() {\n    String str = s.toString();\n    return \"{\" + str.substring(1, str.length() - 1) + \"}\";\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/ast/Primitive.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.ast;\n\nimport edu.harvard.seas.pl.formulog.ast.Terms.TermVisitor;\nimport edu.harvard.seas.pl.formulog.ast.Terms.TermVisitorExn;\nimport edu.harvard.seas.pl.formulog.types.Types.Type;\nimport edu.harvard.seas.pl.formulog.unification.Substitution;\nimport java.util.Map;\nimport java.util.Set;\n\npublic interface Primitive<T> extends Term {\n\n  T getVal();\n\n  @Override\n  default <I, O> O accept(TermVisitor<I, O> v, I in) {\n    return v.visit(this, in);\n  }\n\n  @Override\n  default <I, O, E extends Throwable> O accept(TermVisitorExn<I, O, E> v, I in) throws E {\n    return v.visit(this, in);\n  }\n\n  @Override\n  public default boolean containsUnevaluatedTerm() {\n    return false;\n  }\n\n  @Override\n  public default Term applySubstitution(Substitution s) {\n    return this;\n  }\n\n  @Override\n  public default Term normalize(Substitution s) {\n    return this;\n  }\n\n  @Override\n  public default boolean isGround() {\n    return true;\n  }\n\n  Type getType();\n\n  @Override\n  public default void varSet(Set<Var> acc) {\n    // do nothing\n  }\n\n  @Override\n  public default void updateVarCounts(Map<Var, Integer> counts) {\n    // do nothing\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/ast/Program.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.ast;\n\nimport edu.harvard.seas.pl.formulog.functions.FunctionDef;\nimport edu.harvard.seas.pl.formulog.symbols.ConstructorSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.FunctionSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.RelationSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.SymbolManager;\nimport edu.harvard.seas.pl.formulog.symbols.TypeSymbol;\nimport java.util.Set;\n\npublic interface Program<Q extends Literal, R extends Rule<Q, ?>> {\n\n  Set<FunctionSymbol> getFunctionSymbols();\n\n  Set<RelationSymbol> getFactSymbols();\n\n  Set<RelationSymbol> getRuleSymbols();\n\n  FunctionDef getDef(FunctionSymbol sym);\n\n  Set<Term[]> getFacts(RelationSymbol sym);\n\n  Set<R> getRules(RelationSymbol sym);\n\n  SymbolManager getSymbolManager();\n\n  boolean hasQuery();\n\n  Q getQuery();\n\n  FunctionCallFactory getFunctionCallFactory();\n\n  Set<ConstructorSymbol> getUninterpretedFunctionSymbols();\n\n  Set<TypeSymbol> getTypeSymbols();\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/ast/Rule.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.ast;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic interface Rule<H extends Literal, B extends Literal> extends Iterable<B> {\n\n  H getHead();\n\n  int getBodySize();\n\n  B getBody(int idx);\n\n  default Map<Var, Integer> countVariables() {\n    Map<Var, Integer> m = new HashMap<>();\n    for (Term arg : getHead().getArgs()) {\n      arg.updateVarCounts(m);\n    }\n    for (Literal l : this) {\n      for (Term arg : l.getArgs()) {\n        arg.updateVarCounts(m);\n      }\n    }\n    return Collections.unmodifiableMap(m);\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/ast/SmtLibTerm.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.ast;\n\nimport edu.harvard.seas.pl.formulog.ast.Constructors.SolverVariable;\nimport edu.harvard.seas.pl.formulog.smt.SmtLibShim;\nimport java.util.Set;\nimport org.pcollections.PMap;\n\npublic interface SmtLibTerm extends Term {\n\n  void toSmtLib(SmtLibShim shim);\n\n  SmtLibTerm substSolverTerms(PMap<SolverVariable, SmtLibTerm> subst);\n\n  Set<SolverVariable> freeVars();\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/ast/StringTerm.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.ast;\n\nimport edu.harvard.seas.pl.formulog.ast.Constructors.SolverVariable;\nimport edu.harvard.seas.pl.formulog.smt.SmtLibShim;\nimport edu.harvard.seas.pl.formulog.types.BuiltInTypes;\nimport edu.harvard.seas.pl.formulog.types.Types.Type;\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport org.pcollections.PMap;\n\npublic class StringTerm extends AbstractTerm implements Primitive<String>, SmtLibTerm {\n\n  private static final Map<String, StringTerm> memo = new ConcurrentHashMap<>();\n  private final String val;\n\n  private StringTerm(String val) {\n    this.val = val;\n  }\n\n  public static StringTerm make(String val) {\n    return Util.lookupOrCreate(memo, val, () -> new StringTerm(val));\n  }\n\n  @Override\n  public String getVal() {\n    return val;\n  }\n\n  @Override\n  public String toString() {\n    return \"\\\"\" + val + \"\\\"\";\n  }\n\n  @Override\n  public SmtLibTerm substSolverTerms(PMap<SolverVariable, SmtLibTerm> subst) {\n    return this;\n  }\n\n  @Override\n  public void toSmtLib(SmtLibShim shim) {\n    String s = val.replace(\"\\\"\", \"\\\"\\\"\");\n    shim.print(\"\\\"\" + s + \"\\\"\");\n  }\n\n  @Override\n  public Type getType() {\n    return BuiltInTypes.string;\n  }\n\n  @Override\n  public Set<SolverVariable> freeVars() {\n    return Collections.emptySet();\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/ast/Term.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.ast;\n\nimport edu.harvard.seas.pl.formulog.ast.Terms.TermVisitor;\nimport edu.harvard.seas.pl.formulog.ast.Terms.TermVisitorExn;\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport edu.harvard.seas.pl.formulog.unification.Substitution;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\npublic interface Term {\n\n  <I, O> O accept(TermVisitor<I, O> v, I in);\n\n  <I, O, E extends Throwable> O accept(TermVisitorExn<I, O, E> v, I in) throws E;\n\n  boolean isGround();\n\n  boolean containsUnevaluatedTerm();\n\n  Term applySubstitution(Substitution s);\n\n  Term normalize(Substitution s) throws EvaluationException;\n\n  void varSet(Set<Var> acc);\n\n  default Set<Var> varSet() {\n    Set<Var> vars = new HashSet<>();\n    varSet(vars);\n    return vars;\n  }\n\n  void updateVarCounts(Map<Var, Integer> counts);\n\n  int getId();\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/ast/Terms.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.ast;\n\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport edu.harvard.seas.pl.formulog.symbols.BuiltInConstructorSymbol;\nimport edu.harvard.seas.pl.formulog.unification.Substitution;\nimport edu.harvard.seas.pl.formulog.util.ExceptionalFunction;\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.ListIterator;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.function.Function;\n\npublic final class Terms {\n\n  private Terms() {\n    throw new AssertionError();\n  }\n\n  private static final Term[] EMPTY_TERMS_ARR = new Term[0];\n\n  public static Term[] emptyArray() {\n    return EMPTY_TERMS_ARR;\n  }\n\n  public static Term[] singletonArray(Term t) {\n    return new Term[] {t};\n  }\n\n  public static Term[] map(Term[] ts, Function<Term, Term> f) {\n    Term[] ys = new Term[ts.length];\n    for (int i = 0; i < ts.length; ++i) {\n      ys[i] = f.apply(ts[i]);\n    }\n    return ys;\n  }\n\n  public static <E extends Exception> Term[] mapExn(Term[] ts, ExceptionalFunction<Term, Term, E> f)\n      throws E {\n    Term[] ys = new Term[ts.length];\n    for (int i = 0; i < ts.length; ++i) {\n      ys[i] = f.apply(ts[i]);\n    }\n    return ys;\n  }\n\n  public static Term[] normalize(Term[] ts, Substitution s) throws EvaluationException {\n    Term[] newTs = new Term[ts.length];\n    for (int i = 0; i < ts.length; ++i) {\n      newTs[i] = ts[i].normalize(s);\n    }\n    return newTs;\n  }\n\n  public static boolean isGround(Term t, Set<Var> boundVars) {\n    return boundVars.containsAll(t.varSet());\n  }\n\n  public static Set<Var> getNonBindingVarInstances(Term t) {\n    Set<Var> vars = new HashSet<>();\n    t.accept(\n        new TermVisitor<Void, Void>() {\n\n          @Override\n          public Void visit(Var t, Void in) {\n            return null;\n          }\n\n          @Override\n          public Void visit(Constructor t, Void in) {\n            for (Term arg : t.getArgs()) {\n              arg.accept(this, in);\n            }\n            return null;\n          }\n\n          @Override\n          public Void visit(Primitive<?> prim, Void in) {\n            return null;\n          }\n\n          @Override\n          public Void visit(Expr expr, Void in) {\n            vars.addAll(expr.varSet());\n            return null;\n          }\n        },\n        null);\n    return vars;\n  }\n\n  public static Set<Var> getBindingVarInstances(Term t) {\n    Set<Var> vars = new HashSet<>();\n    t.accept(\n        new TermVisitor<Void, Void>() {\n\n          @Override\n          public Void visit(Var t, Void in) {\n            vars.add(t);\n            return null;\n          }\n\n          @Override\n          public Void visit(Constructor t, Void in) {\n            for (Term arg : t.getArgs()) {\n              arg.accept(this, in);\n            }\n            return null;\n          }\n\n          @Override\n          public Void visit(Primitive<?> prim, Void in) {\n            return null;\n          }\n\n          @Override\n          public Void visit(Expr expr, Void in) {\n            return null;\n          }\n        },\n        null);\n    return vars;\n  }\n\n  public static interface TermVisitor<I, O> {\n\n    O visit(Var t, I in);\n\n    O visit(Constructor c, I in);\n\n    O visit(Primitive<?> p, I in);\n\n    O visit(Expr e, I in);\n  }\n\n  public static interface TermVisitorExn<I, O, E extends Throwable> {\n\n    O visit(Var x, I in) throws E;\n\n    O visit(Constructor c, I in) throws E;\n\n    O visit(Primitive<?> p, I in) throws E;\n\n    O visit(Expr e, I in) throws E;\n  }\n\n  public static final Term minTerm = new DummyTerm(Integer.MIN_VALUE);\n\n  public static final Term maxTerm = new DummyTerm(Integer.MAX_VALUE);\n\n  private static class DummyTerm implements Term {\n\n    private final int id;\n\n    public DummyTerm(int id) {\n      this.id = id;\n    }\n\n    @Override\n    public <I, O> O accept(TermVisitor<I, O> v, I in) {\n      throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public <I, O, E extends Throwable> O accept(TermVisitorExn<I, O, E> v, I in) throws E {\n      throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public boolean isGround() {\n      return true;\n    }\n\n    @Override\n    public boolean containsUnevaluatedTerm() {\n      return false;\n    }\n\n    @Override\n    public Term applySubstitution(Substitution s) {\n      throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public Term normalize(Substitution s) throws EvaluationException {\n      throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public void varSet(Set<Var> acc) {\n      throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public int getId() {\n      return id;\n    }\n\n    @Override\n    public void updateVarCounts(Map<Var, Integer> counts) {\n      throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public String toString() {\n      return \"DummyTerm(\" + id + \")\";\n    }\n  }\n\n  public static Term makeDummyTerm(int id) {\n    return new DummyTerm(id);\n  }\n\n  private static final AtomicInteger idCnt = new AtomicInteger(0);\n\n  public static int nextId() {\n    return idCnt.incrementAndGet();\n  }\n\n  @SuppressWarnings(\"unchecked\")\n  public static <T extends Term> List<T> termToTermList(Term t) {\n    List<T> xs = new ArrayList<>();\n    Constructor c = (Constructor) t;\n    while (c.getSymbol() == BuiltInConstructorSymbol.CONS) {\n      Term[] args = c.getArgs();\n      xs.add((T) args[0]);\n      c = (Constructor) args[1];\n    }\n    assert c.getSymbol() == BuiltInConstructorSymbol.NIL;\n    return xs;\n  }\n\n  public static <T extends Term> Term termListToTerm(List<T> l) {\n    Term acc = Constructors.nil();\n    for (ListIterator<T> it = l.listIterator(l.size()); it.hasPrevious(); ) {\n      acc = Constructors.cons(it.previous(), acc);\n    }\n    return acc;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/ast/UnificationPredicate.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.ast;\n\nimport edu.harvard.seas.pl.formulog.ast.ComplexLiterals.ComplexLiteralExnVisitor;\nimport edu.harvard.seas.pl.formulog.ast.ComplexLiterals.ComplexLiteralVisitor;\nimport edu.harvard.seas.pl.formulog.unification.Substitution;\nimport java.util.Arrays;\n\npublic class UnificationPredicate implements ComplexLiteral {\n\n  private final Term[] args;\n  private final boolean negated;\n\n  public static UnificationPredicate make(Term lhs, Term rhs, boolean negated) {\n    return new UnificationPredicate(new Term[] {lhs, rhs}, negated);\n  }\n\n  private UnificationPredicate(Term[] args, boolean negated) {\n    this.args = args;\n    this.negated = negated;\n  }\n\n  @Override\n  public Term[] getArgs() {\n    return args;\n  }\n\n  public Term getLhs() {\n    return args[0];\n  }\n\n  public Term getRhs() {\n    return args[1];\n  }\n\n  @Override\n  public UnificationPredicate applySubstitution(Substitution subst) {\n    Term newLhs = getLhs().applySubstitution(subst);\n    Term newRhs = getRhs().applySubstitution(subst);\n    return make(newLhs, newRhs, negated);\n  }\n\n  @Override\n  public boolean isNegated() {\n    return negated;\n  }\n\n  @Override\n  public String toString() {\n    return getLhs() + (negated ? \" != \" : \" = \") + getRhs();\n  }\n\n  @Override\n  public <I, O> O accept(ComplexLiteralVisitor<I, O> visitor, I input) {\n    return visitor.visit(this, input);\n  }\n\n  @Override\n  public <I, O, E extends Throwable> O accept(ComplexLiteralExnVisitor<I, O, E> visitor, I input)\n      throws E {\n    return visitor.visit(this, input);\n  }\n\n  @Override\n  public int hashCode() {\n    final int prime = 31;\n    int result = 1;\n    result = prime * result + Arrays.hashCode(args);\n    result = prime * result + (negated ? 1231 : 1237);\n    return result;\n  }\n\n  @Override\n  public boolean equals(Object obj) {\n    if (this == obj) return true;\n    if (obj == null) return false;\n    if (getClass() != obj.getClass()) return false;\n    UnificationPredicate other = (UnificationPredicate) obj;\n    if (!Arrays.equals(args, other.args)) return false;\n    if (negated != other.negated) return false;\n    return true;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/ast/UserPredicate.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.ast;\n\nimport edu.harvard.seas.pl.formulog.ast.ComplexLiterals.ComplexLiteralExnVisitor;\nimport edu.harvard.seas.pl.formulog.ast.ComplexLiterals.ComplexLiteralVisitor;\nimport edu.harvard.seas.pl.formulog.symbols.RelationSymbol;\nimport edu.harvard.seas.pl.formulog.unification.Substitution;\nimport java.util.Arrays;\n\npublic class UserPredicate implements ComplexLiteral {\n\n  private final RelationSymbol symbol;\n  private final Term[] args;\n  private final boolean negated;\n\n  public static UserPredicate make(RelationSymbol symbol, Term[] args, boolean negated) {\n    return new UserPredicate(symbol, args, negated);\n  }\n\n  private UserPredicate(RelationSymbol symbol, Term[] args, boolean negated) {\n    this.symbol = symbol;\n    this.args = args;\n    this.negated = negated;\n  }\n\n  public RelationSymbol getSymbol() {\n    return symbol;\n  }\n\n  @Override\n  public Term[] getArgs() {\n    return args;\n  }\n\n  @Override\n  public boolean isNegated() {\n    return negated;\n  }\n\n  @Override\n  public UserPredicate applySubstitution(Substitution subst) {\n    Term[] newArgs = new Term[args.length];\n    for (int i = 0; i < args.length; ++i) {\n      newArgs[i] = args[i].applySubstitution(subst);\n    }\n    return make(symbol, newArgs, negated);\n  }\n\n  @Override\n  public int hashCode() {\n    final int prime = 31;\n    int result = 1;\n    result = prime * result + Arrays.hashCode(args);\n    result = prime * result + (negated ? 1231 : 1237);\n    result = prime * result + ((symbol == null) ? 0 : symbol.hashCode());\n    return result;\n  }\n\n  @Override\n  public boolean equals(Object obj) {\n    if (this == obj) return true;\n    if (obj == null) return false;\n    if (getClass() != obj.getClass()) return false;\n    UserPredicate other = (UserPredicate) obj;\n    if (!Arrays.equals(args, other.args)) return false;\n    if (negated != other.negated) return false;\n    if (symbol == null) {\n      if (other.symbol != null) return false;\n    } else if (!symbol.equals(other.symbol)) return false;\n    return true;\n  }\n\n  @Override\n  public String toString() {\n    StringBuilder sb = new StringBuilder();\n    if (negated) {\n      sb.append(\"!\");\n    }\n    sb.append(symbol);\n    int arity = args.length;\n    if (arity > 0) {\n      sb.append('(');\n      for (int i = 0; i < arity; ++i) {\n        sb.append(args[i]);\n        if (i < arity - 1) {\n          sb.append(\", \");\n        }\n      }\n      sb.append(')');\n    }\n    return sb.toString();\n  }\n\n  @Override\n  public <I, O> O accept(ComplexLiteralVisitor<I, O> visitor, I input) {\n    return visitor.visit(this, input);\n  }\n\n  @Override\n  public <I, O, E extends Throwable> O accept(ComplexLiteralExnVisitor<I, O, E> visitor, I input)\n      throws E {\n    return visitor.visit(this, input);\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/ast/Var.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.ast;\n\nimport edu.harvard.seas.pl.formulog.ast.Terms.TermVisitor;\nimport edu.harvard.seas.pl.formulog.ast.Terms.TermVisitorExn;\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport edu.harvard.seas.pl.formulog.unification.Substitution;\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.atomic.AtomicInteger;\n\npublic class Var extends AbstractTerm {\n\n  static final AtomicInteger cnt = new AtomicInteger();\n\n  private final String name;\n\n  protected Var(String name) {\n    this.name = name;\n  }\n\n  public static Var fresh(String name) {\n    return new Var(name);\n  }\n\n  public static Var fresh() {\n    return new Var(\"$\" + cnt.getAndIncrement());\n  }\n\n  public boolean isUnderscore() {\n    return name.equals(\"_\");\n  }\n\n  public String getName() {\n    return name;\n  }\n\n  @Override\n  public String toString() {\n    return name;\n  }\n\n  @Override\n  public <I, O> O accept(TermVisitor<I, O> v, I in) {\n    return v.visit(this, in);\n  }\n\n  @Override\n  public <I, O, E extends Throwable> O accept(TermVisitorExn<I, O, E> v, I in) throws E {\n    return v.visit(this, in);\n  }\n\n  @Override\n  public boolean isGround() {\n    return false;\n  }\n\n  @Override\n  public boolean containsUnevaluatedTerm() {\n    return false;\n  }\n\n  @Override\n  public Term applySubstitution(Substitution s) {\n    if (s.containsKey(this)) {\n      return s.get(this);\n    }\n    return this;\n  }\n\n  @Override\n  public Term normalize(Substitution s) throws EvaluationException {\n    assert s.containsKey(this);\n    return s.get(this);\n  }\n\n  @Override\n  public void varSet(Set<Var> acc) {\n    acc.add(this);\n  }\n\n  @Override\n  public void updateVarCounts(Map<Var, Integer> counts) {\n    int n = Util.lookupOrCreate(counts, this, () -> 0);\n    counts.put(this, n + 1);\n  }\n\n  private static final Var hole = new Var(\"??\");\n\n  public static Var makeHole() {\n    return hole;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/CodeGen.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2020-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen;\n\nimport edu.harvard.seas.pl.formulog.Configuration;\nimport edu.harvard.seas.pl.formulog.ast.BasicProgram;\nimport edu.harvard.seas.pl.formulog.ast.BasicRule;\nimport edu.harvard.seas.pl.formulog.ast.Program;\nimport edu.harvard.seas.pl.formulog.ast.UserPredicate;\nimport edu.harvard.seas.pl.formulog.magic.MagicSetTransformer;\nimport edu.harvard.seas.pl.formulog.parsing.Parser;\nimport edu.harvard.seas.pl.formulog.symbols.GlobalSymbolManager;\nimport edu.harvard.seas.pl.formulog.symbols.parameterized.BuiltInConstructorSymbolBase;\nimport edu.harvard.seas.pl.formulog.symbols.parameterized.Param;\nimport edu.harvard.seas.pl.formulog.symbols.parameterized.ParamKind;\nimport edu.harvard.seas.pl.formulog.symbols.parameterized.ParameterizedConstructorSymbol;\nimport edu.harvard.seas.pl.formulog.types.BuiltInTypes;\nimport edu.harvard.seas.pl.formulog.types.TypeChecker;\nimport edu.harvard.seas.pl.formulog.types.WellTypedProgram;\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport java.io.File;\nimport java.io.FileReader;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.URISyntaxException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.StandardCopyOption;\n\npublic class CodeGen {\n\n  private final BasicProgram prog;\n  private final File outDir;\n\n  public CodeGen(BasicProgram prog, File outDir) {\n    this.prog = prog;\n    this.outDir = outDir;\n  }\n\n  public void go() throws IOException, URISyntaxException, CodeGenException {\n    CodeGenContext ctx = new CodeGenContext(prog);\n    // Make sure that we generate a symbol for SMT variables (assumed in the C++\n    // runtime code)\n    ParameterizedConstructorSymbol sym =\n        GlobalSymbolManager.getParameterizedSymbol(BuiltInConstructorSymbolBase.SMT_VAR)\n            .copyWithNewArgs(\n                new Param(BuiltInTypes.bool, ParamKind.ANY_TYPE),\n                new Param(BuiltInTypes.bool, ParamKind.PRE_SMT_TYPE));\n    String repr = ctx.lookupRepr(sym);\n    assert repr.equals(\"Symbol::smt_var__bool__bool\") : repr;\n\n    copy(\"CMakeLists.txt\");\n    copySrc(\"time.hpp\");\n    copySrc(\"ConcurrentHashMap.hpp\");\n    new FuncsHpp(ctx).gen(outDir);\n    new MainCpp(ctx).gen(outDir);\n    new SouffleCodeGen(ctx).gen(outDir);\n    copySrc(\"smt_solver.h\");\n    copySrc(\"smt_solver.cpp\");\n    copySrc(\"smt_shim.h\");\n    new SmtShimCpp(ctx).gen(outDir);\n    copySrc(\"smt_parser.hpp\");\n    new SmtParserCpp(ctx).gen(outDir);\n    copySrc(\"Type.hpp\");\n    new TypeCpp(ctx).gen(outDir);\n    copySrc(\"Term.hpp\");\n    new TermCpp(ctx).gen(outDir);\n    copySrc(\"Tuple.hpp\");\n    copySrc(\"parser.hpp\");\n    copySrc(\"parser.cpp\");\n    new SymbolHpp(ctx).gen(outDir);\n    new SymbolCpp(ctx).gen(outDir);\n    copySrc(\"globals.h\");\n    copySrc(\"set.cpp\");\n    copySrc(\"set.hpp\");\n  }\n\n  private void copy(String name) throws IOException {\n    var inFile = Path.of(\"codegen\", name).toString();\n    try (InputStream is = getClass().getClassLoader().getResourceAsStream(inFile)) {\n      assert is != null : name;\n      Files.copy(is, outDir.toPath().resolve(name), StandardCopyOption.REPLACE_EXISTING);\n    }\n  }\n\n  private void copySrc(String name) throws IOException {\n    copy(Path.of(\"src\", name).toString());\n  }\n\n  public static void main(String[] args) throws Exception {\n    if (args.length != 1) {\n      throw new IllegalArgumentException(\"Expected a single argument (the Formulog source file)\");\n    }\n    main(new File(args[0]), new File(\"codegen\"));\n  }\n\n  public static void main(File file, File outDir) throws Exception {\n    Program<UserPredicate, BasicRule> prog;\n    try (FileReader fr = new FileReader(file)) {\n      prog = new Parser().parse(fr);\n    }\n    WellTypedProgram wtp = new TypeChecker(prog).typeCheck();\n    MagicSetTransformer mst = new MagicSetTransformer(wtp);\n    BasicProgram magicProg =\n        mst.transform(Configuration.useDemandTransformation, Configuration.restoreStratification);\n    File srcDir = outDir.toPath().resolve(\"src\").toFile();\n    srcDir.mkdirs();\n    Util.clean(srcDir, false);\n    new CodeGen(magicProg, outDir).go();\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/CodeGenContext.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2020-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen;\n\nimport edu.harvard.seas.pl.formulog.ast.BasicProgram;\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.codegen.ast.souffle.SFunctorBody;\nimport edu.harvard.seas.pl.formulog.codegen.ast.souffle.SIntListType;\nimport edu.harvard.seas.pl.formulog.codegen.ast.souffle.SRuleMode;\nimport edu.harvard.seas.pl.formulog.symbols.*;\nimport edu.harvard.seas.pl.formulog.types.Types.AlgebraicDataType;\nimport edu.harvard.seas.pl.formulog.types.Types.AlgebraicDataType.ConstructorScheme;\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.stream.Collectors;\n\npublic class CodeGenContext {\n\n  private final Map<ConstructorSymbol, String> ctorSymToRepr = new HashMap<>();\n  private final Map<FunctionSymbol, String> funcSymToRepr = new HashMap<>();\n  private final AtomicInteger id = new AtomicInteger();\n  private final Map<String, SFunctorBody> functorBody = new ConcurrentHashMap<>();\n  private final Set<SIntListType> souffleTypes = new HashSet<>();\n  private final Map<String, Pair<Integer, SRuleMode>> customRelations = new HashMap<>();\n  private final Map<Term, String> exprFunctorNames = new HashMap<>();\n  private final Map<ConstructorSymbol, String> dtorFunctorNames = new HashMap<>();\n  private final Map<String, RelationSymbol> reprToRelSym = new HashMap<>();\n\n  private final BasicProgram prog;\n\n  public CodeGenContext(BasicProgram prog) {\n    this.prog = prog;\n    new Worker().go();\n  }\n\n  public BasicProgram getProgram() {\n    return prog;\n  }\n\n  public Set<ConstructorSymbol> getConstructorSymbols() {\n    return Collections.unmodifiableSet(ctorSymToRepr.keySet());\n  }\n\n  public synchronized String lookupUnqualifiedRepr(ConstructorSymbol sym) {\n    String repr = ctorSymToRepr.get(sym);\n    if (repr == null) {\n      repr = CodeGenUtil.mkName(sym);\n      String repr2 = ctorSymToRepr.putIfAbsent(sym, repr);\n      assert repr2 == null;\n    }\n    return repr;\n  }\n\n  public synchronized String lookupRepr(ConstructorSymbol sym) {\n    return \"Symbol::\" + lookupUnqualifiedRepr(sym);\n  }\n\n  public synchronized String lookupRepr(FunctionSymbol sym) {\n    String repr = funcSymToRepr.get(sym);\n    assert repr != null : sym;\n    return \"funcs::\" + repr;\n  }\n\n  public synchronized String lookupRepr(RelationSymbol sym) {\n    var repr = sym.toString().replace(\":\", \"__\") + \"_\";\n    reprToRelSym.put(repr, sym);\n    return repr;\n  }\n\n  public synchronized RelationSymbol lookupRel(String repr) {\n    return reprToRelSym.get(repr);\n  }\n\n  public synchronized void register(FunctionSymbol sym, String repr) {\n    String repr2 = funcSymToRepr.put(sym, repr);\n    assert repr2 == null || repr2.equals(repr);\n  }\n\n  public synchronized void register(SIntListType type) {\n    souffleTypes.add(type);\n  }\n\n  public synchronized Set<SIntListType> getSouffleTypes() {\n    return Collections.unmodifiableSet(souffleTypes);\n  }\n\n  public String newId(String prefix) {\n    return prefix + id.getAndIncrement();\n  }\n\n  public synchronized String lookupOrCreateFunctorName(Term t) {\n    return Util.lookupOrCreate(exprFunctorNames, t, () -> \"expr_\" + id.getAndIncrement());\n  }\n\n  public synchronized String lookupOrCreateFunctorName(ConstructorSymbol sym) {\n    return Util.lookupOrCreate(dtorFunctorNames, sym, () -> \"dtor_\" + id.getAndIncrement());\n  }\n\n  public void registerFunctorBody(String functor, SFunctorBody body) {\n    functorBody.put(functor, body);\n  }\n\n  public Set<Pair<String, SFunctorBody>> getFunctors() {\n    Set<Pair<String, SFunctorBody>> s = new HashSet<>();\n    for (Map.Entry<String, SFunctorBody> e : functorBody.entrySet()) {\n      s.add(new Pair<>(e.getKey(), e.getValue()));\n    }\n    return Collections.unmodifiableSet(s);\n  }\n\n  public Set<Pair<String, Pair<Integer, SRuleMode>>> getCustomRelations() {\n    return customRelations.entrySet().stream()\n        .map(e -> new Pair<>(e.getKey(), e.getValue()))\n        .collect(Collectors.toSet());\n  }\n\n  public void registerCustomRelation(String name, Integer arity, SRuleMode mode) {\n    var p = new Pair<>(arity, mode);\n    var other = customRelations.put(name, p);\n    assert other == null || other.equals(p);\n  }\n\n  private class Worker {\n\n    public Worker() {}\n\n    public void go() {\n      processTypes(prog.getTypeSymbols());\n      for (ConstructorSymbol sym : BuiltInConstructorSymbol.values()) {\n        lookupRepr(sym);\n      }\n    }\n\n    private void processTypes(Set<TypeSymbol> typeSymbols) {\n      for (TypeSymbol sym : typeSymbols) {\n        if (!sym.isAlias()) {\n          processType(AlgebraicDataType.makeWithFreshArgs(sym));\n        }\n      }\n    }\n\n    private void processType(AlgebraicDataType type) {\n      if (type.hasConstructors()) {\n        for (ConstructorScheme cs : type.getConstructors()) {\n          ConstructorSymbol sym = cs.getSymbol();\n          lookupRepr(sym);\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/CodeGenException.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2022-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen;\n\npublic class CodeGenException extends Exception {\n\n  private static final long serialVersionUID = -3022070471185499421L;\n\n  public CodeGenException(String message) {\n    super(message);\n  }\n\n  public CodeGenException(Exception cause) {\n    super(cause);\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/CodeGenUtil.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen;\n\nimport edu.harvard.seas.pl.formulog.codegen.ast.cpp.CppAccess;\nimport edu.harvard.seas.pl.formulog.codegen.ast.cpp.CppCast;\nimport edu.harvard.seas.pl.formulog.codegen.ast.cpp.CppConst;\nimport edu.harvard.seas.pl.formulog.codegen.ast.cpp.CppExpr;\nimport edu.harvard.seas.pl.formulog.codegen.ast.cpp.CppStmt;\nimport edu.harvard.seas.pl.formulog.codegen.ast.cpp.CppSubscript;\nimport edu.harvard.seas.pl.formulog.codegen.ast.cpp.CppUnop;\nimport edu.harvard.seas.pl.formulog.symbols.BuiltInFunctionSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.FunctionSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.Symbol;\nimport edu.harvard.seas.pl.formulog.symbols.parameterized.Param;\nimport edu.harvard.seas.pl.formulog.symbols.parameterized.ParameterizedSymbol;\nimport java.io.BufferedReader;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\nimport java.nio.file.Path;\nimport java.util.Iterator;\n\npublic final class CodeGenUtil {\n\n  private CodeGenUtil() {\n    throw new AssertionError(\"impossible\");\n  }\n\n  public static void printIndent(PrintWriter out, int indent) {\n    for (int i = 0; i < indent; ++i) {\n      out.print(\"  \");\n    }\n  }\n\n  public static void print(Iterable<CppStmt> stmts, PrintWriter out, int indent) {\n    for (CppStmt stmt : stmts) {\n      stmt.println(out, indent);\n    }\n  }\n\n  public static void printSeparated(Iterable<CppExpr> exprs, String sep, PrintWriter out) {\n    for (Iterator<CppExpr> it = exprs.iterator(); it.hasNext(); ) {\n      it.next().print(out);\n      if (it.hasNext()) {\n        out.print(sep);\n      }\n    }\n  }\n\n  public static CppExpr mkComplexTermLookup(CppExpr base, int offset) {\n    CppExpr cast = CppCast.mkReinterpret(\"const ComplexTerm&\", CppUnop.mkDeref(base));\n    CppExpr access = CppAccess.mk(cast, \"val\");\n    return CppSubscript.mk(access, CppConst.mkInt(offset));\n  }\n\n  public static void copyOver(BufferedReader in, PrintWriter out, int stopAt) throws IOException {\n    String line;\n    while ((line = in.readLine()) != null && !line.equals(\"/* INSERT \" + stopAt + \" */\")) {\n      out.println(line);\n    }\n  }\n\n  public static String mkName(Symbol sym) {\n    String s;\n    if (sym instanceof ParameterizedSymbol) {\n      ParameterizedSymbol psym = (ParameterizedSymbol) sym;\n      StringBuilder sb = new StringBuilder(psym.getBase().toString());\n      for (Param p : psym.getArgs()) {\n        sb.append(\"__\");\n        String ty = p.getType().toString().replaceAll(\"[^A-Za-z0-9_]+\", \"_\");\n        sb.append(ty);\n      }\n      s = sb.toString();\n    } else {\n      s = sym.toString().replaceAll(\"[^A-Za-z0-9_]+\", \"_\");\n    }\n    if (sym instanceof FunctionSymbol && !(sym instanceof BuiltInFunctionSymbol)) {\n      s = \"FLG_\" + s;\n    }\n    return s;\n  }\n\n  public static String toString(CppStmt stmt, int indent) {\n    StringWriter sw = new StringWriter();\n    stmt.println(new PrintWriter(sw), indent);\n    return sw.toString();\n  }\n\n  public static InputStream inputSrcFile(String file) {\n    var cl = CodeGenUtil.class.getClassLoader();\n    var path = Path.of(\"codegen\", \"src\", file).toString();\n    var is = cl.getResourceAsStream(path);\n    assert is != null : path;\n    return is;\n  }\n\n  public static File outputSrcFile(File topDir, String file) {\n    return topDir.toPath().resolve(\"src\").resolve(file).toFile();\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/DeltaFirstQueryPlanner.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen;\n\nimport edu.harvard.seas.pl.formulog.codegen.ast.souffle.SAtom;\nimport edu.harvard.seas.pl.formulog.codegen.ast.souffle.SRule;\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport edu.harvard.seas.pl.formulog.validating.Stratum;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\npublic class DeltaFirstQueryPlanner implements QueryPlanner {\n\n  private final CodeGenContext ctx;\n\n  public DeltaFirstQueryPlanner(CodeGenContext ctx) {\n    this.ctx = ctx;\n  }\n\n  @Override\n  public List<Pair<Integer, int[]>> genPlan(SRule r, Stratum stratum) {\n    List<Pair<Integer, int[]>> l = new ArrayList<>();\n    var p = findDeltas(r, stratum);\n    var numAtoms = p.fst();\n    int i = 0;\n    for (var deltaPos : p.snd()) {\n      l.add(new Pair<>(i++, genPlanForDelta(deltaPos, numAtoms)));\n    }\n    return l;\n  }\n\n  private Pair<Integer, List<Integer>> findDeltas(SRule r, Stratum stratum) {\n    List<Integer> l = new ArrayList<>();\n    var body = r.getBody();\n    var preds = stratum.getPredicateSyms();\n    int numAtoms = 0;\n    for (int i = 0; i < body.size(); ++i) {\n      var lit = body.get(i);\n      if (lit instanceof SAtom) {\n        numAtoms++;\n        var pred = ((SAtom) lit).getSymbol();\n        var rel = ctx.lookupRel(pred);\n        if (rel != null && preds.contains(rel)) {\n          l.add(i + 1);\n        }\n      }\n    }\n    return new Pair<>(numAtoms, l);\n  }\n\n  private int[] genPlanForDelta(int delta, int numAtoms) {\n    int[] arr = new int[numAtoms];\n    Arrays.setAll(\n        arr,\n        (i) -> {\n          if (i == 0) {\n            return delta;\n          } else if (i < delta) {\n            return i;\n          } else {\n            return i + 1;\n          }\n        });\n    return arr;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/FuncsHpp.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen;\n\nimport edu.harvard.seas.pl.formulog.ast.BindingType;\nimport edu.harvard.seas.pl.formulog.ast.Var;\nimport edu.harvard.seas.pl.formulog.codegen.ast.cpp.*;\nimport edu.harvard.seas.pl.formulog.functions.*;\nimport edu.harvard.seas.pl.formulog.symbols.*;\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\npublic class FuncsHpp extends TemplateSrcFile {\n\n  public FuncsHpp(CodeGenContext ctx) {\n    super(\"funcs.hpp\", ctx);\n  }\n\n  public void gen(BufferedReader br, PrintWriter out) throws IOException {\n    Worker pr = new Worker(out);\n    CodeGenUtil.copyOver(br, out, 0);\n    pr.makeDeclarations();\n    CodeGenUtil.copyOver(br, out, 1);\n    pr.makeDefinitions();\n    CodeGenUtil.copyOver(br, out, -1);\n  }\n\n  private class Worker {\n\n    private final PrintWriter out;\n    private final Set<FunctionSymbol> userDefinedFunctions = new HashSet<>();\n    private final Set<PredicateFunctionSymbol> predFunctions = new HashSet<>();\n    private final TermCodeGen tcg = new TermCodeGen(ctx);\n\n    public Worker(PrintWriter out) {\n      this.out = out;\n    }\n\n    private void makeDeclarations() {\n      for (FunctionSymbol sym : ctx.getProgram().getFunctionSymbols()) {\n        if (sym instanceof BuiltInFunctionSymbol) {\n          registerBuiltInFunction((BuiltInFunctionSymbol) sym);\n        } else if (sym instanceof PredicateFunctionSymbol) {\n          predFunctions.add((PredicateFunctionSymbol) sym);\n          declareFunction(sym);\n        } else {\n          FunctionDef def = ctx.getProgram().getDef(sym);\n          if (def instanceof UserFunctionDef) {\n            userDefinedFunctions.add(sym);\n            declareFunction(sym);\n          } else if (def instanceof RecordAccessor) {\n            ctx.register(sym, \"__access<\" + ((RecordAccessor) def).getIndex() + \">\");\n          } else {\n            throw new AssertionError(\"Unexpected function def: \" + sym + \" \" + def.getClass());\n          }\n        }\n      }\n    }\n\n    private void declareFunction(FunctionSymbol sym) {\n      String name = CodeGenUtil.mkName(sym);\n      ctx.register(sym, name);\n      out.println();\n      out.print(\"term_ptr \" + name + \"(\");\n      int n = sym.getArity();\n      for (int i = 0; i < n; ++i) {\n        out.print(\"term_ptr\");\n        if (i < n - 1) {\n          out.print(\", \");\n        }\n      }\n      out.println(\");\");\n    }\n\n    private void registerBuiltInFunction(BuiltInFunctionSymbol sym) {\n      switch (sym) {\n        case BEQ:\n          ctx.register(sym, \"beq\");\n          break;\n        case BNEQ:\n          ctx.register(sym, \"bneq\");\n          break;\n        case BNOT:\n          ctx.register(sym, \"bnot\");\n          break;\n        case FP32_ADD:\n          ctx.register(sym, \"__add<float>\");\n          break;\n        case FP32_DIV:\n          ctx.register(sym, \"__div<float>\");\n          break;\n        case FP32_EQ:\n          ctx.register(sym, \"__eq<float>\");\n          break;\n        case FP32_GE:\n          ctx.register(sym, \"__ge<float>\");\n          break;\n        case FP32_GT:\n          ctx.register(sym, \"__gt<float>\");\n          break;\n        case FP32_LE:\n          ctx.register(sym, \"__le<float>\");\n          break;\n        case FP32_LT:\n          ctx.register(sym, \"__lt<float>\");\n          break;\n        case FP32_MUL:\n          ctx.register(sym, \"__mul<float>\");\n          break;\n        case FP32_NEG:\n          ctx.register(sym, \"__neg<float>\");\n          break;\n        case FP32_REM:\n          ctx.register(sym, \"__rem<float>\");\n          break;\n        case FP32_SUB:\n          ctx.register(sym, \"__sub<float>\");\n          break;\n        case FP64_ADD:\n          ctx.register(sym, \"__add<double>\");\n          break;\n        case FP64_DIV:\n          ctx.register(sym, \"__div<double>\");\n          break;\n        case FP64_EQ:\n          ctx.register(sym, \"__eq<double>\");\n          break;\n        case FP64_GE:\n          ctx.register(sym, \"__ge<double>\");\n          break;\n        case FP64_GT:\n          ctx.register(sym, \"__gt<double>\");\n          break;\n        case FP64_LE:\n          ctx.register(sym, \"__le<double>\");\n          break;\n        case FP64_LT:\n          ctx.register(sym, \"__lt<double>\");\n          break;\n        case FP64_MUL:\n          ctx.register(sym, \"__mul<double>\");\n          break;\n        case FP64_NEG:\n          ctx.register(sym, \"__neg<double>\");\n          break;\n        case FP64_REM:\n          ctx.register(sym, \"__rem<double>\");\n          break;\n        case FP64_SUB:\n          ctx.register(sym, \"__sub<double>\");\n          break;\n        case I32_ADD:\n          ctx.register(sym, \"__add<int32_t>\");\n          break;\n        case I32_AND:\n          ctx.register(sym, \"__bitwise_and<int32_t>\");\n          break;\n        case I32_SDIV:\n          ctx.register(sym, \"__div<int32_t>\");\n          break;\n        case I32_GE:\n          ctx.register(sym, \"__ge<int32_t>\");\n          break;\n        case I32_GT:\n          ctx.register(sym, \"__gt<int32_t>\");\n          break;\n        case I32_LE:\n          ctx.register(sym, \"__le<int32_t>\");\n          break;\n        case I32_LT:\n          ctx.register(sym, \"__lt<int32_t>\");\n          break;\n        case I32_MUL:\n          ctx.register(sym, \"__mul<int32_t>\");\n          break;\n        case I32_NEG:\n          ctx.register(sym, \"__neg<int32_t>\");\n          break;\n        case I32_OR:\n          ctx.register(sym, \"__bitwise_or<int32_t>\");\n          break;\n        case I32_SREM:\n          ctx.register(sym, \"__rem<int32_t>\");\n          break;\n        case I32_SCMP:\n          ctx.register(sym, \"__cmp<int32_t>\");\n          break;\n        case I32_SUB:\n          ctx.register(sym, \"__sub<int32_t>\");\n          break;\n        case I32_UCMP:\n          ctx.register(sym, \"__cmp<uint32_t>\");\n          break;\n        case I32_XOR:\n          ctx.register(sym, \"__bitwise_xor<int32_t>\");\n          break;\n        case I32_SHL:\n          ctx.register(sym, \"__shl<int32_t>\");\n          break;\n        case I32_ASHR:\n          ctx.register(sym, \"__ashr<int32_t>\");\n          break;\n        case I32_LSHR:\n          ctx.register(sym, \"__lshr<int32_t, uint32_t>\");\n          break;\n        case I32_UREM:\n          ctx.register(sym, \"__urem<int32_t, uint32_t>\");\n          break;\n        case I32_UDIV:\n          ctx.register(sym, \"__udiv<int32_t, uint32_t>\");\n          break;\n        case I64_ADD:\n          ctx.register(sym, \"__add<int64_t>\");\n          break;\n        case I64_AND:\n          ctx.register(sym, \"__bitwise_and<int64_t>\");\n          break;\n        case I64_SDIV:\n          ctx.register(sym, \"__div<int64_t>\");\n          break;\n        case I64_GE:\n          ctx.register(sym, \"__ge<int64_t>\");\n          break;\n        case I64_GT:\n          ctx.register(sym, \"__gt<int64_t>\");\n          break;\n        case I64_LE:\n          ctx.register(sym, \"__le<int64_t>\");\n          break;\n        case I64_LT:\n          ctx.register(sym, \"__lt<int64_t>\");\n          break;\n        case I64_MUL:\n          ctx.register(sym, \"__mul<int64_t>\");\n          break;\n        case I64_NEG:\n          ctx.register(sym, \"__neg<int64_t>\");\n          break;\n        case I64_OR:\n          ctx.register(sym, \"__bitwise_or<int64_t>\");\n          break;\n        case I64_SREM:\n          ctx.register(sym, \"__rem<int64_t>\");\n          break;\n        case I64_SCMP:\n          ctx.register(sym, \"__cmp<int64_t>\");\n          break;\n        case I64_SUB:\n          ctx.register(sym, \"__sub<int64_t>\");\n          break;\n        case I64_UCMP:\n          ctx.register(sym, \"__cmp<uint64_t>\");\n          break;\n        case I64_XOR:\n          ctx.register(sym, \"__bitwise_xor<int64_t>\");\n          break;\n        case I64_SHL:\n          ctx.register(sym, \"__shl<int64_t>\");\n          break;\n        case I64_ASHR:\n          ctx.register(sym, \"__ashr<int64_t>\");\n          break;\n        case I64_LSHR:\n          ctx.register(sym, \"__lshr<int64_t, uint64_t>\");\n          break;\n        case I64_UREM:\n          ctx.register(sym, \"__urem<int64_t, uint64_t>\");\n          break;\n        case I64_UDIV:\n          ctx.register(sym, \"__udiv<int64_t, uint64_t>\");\n          break;\n        case IS_SAT:\n          ctx.register(sym, \"is_sat\");\n          break;\n        case IS_SAT_OPT:\n          ctx.register(sym, \"is_sat_opt\");\n          break;\n        case IS_VALID:\n          ctx.register(sym, \"is_valid\");\n          break;\n        case GET_MODEL:\n          ctx.register(sym, \"get_model\");\n          break;\n        case QUERY_MODEL:\n          ctx.register(sym, \"query_model\");\n          break;\n        case PRINT:\n          ctx.register(sym, \"print\");\n          break;\n        case STRING_CONCAT:\n          ctx.register(sym, \"string_concat\");\n          break;\n        case STRING_CMP:\n          ctx.register(sym, \"__cmp<string>\");\n          break;\n        case STRING_MATCHES:\n          ctx.register(sym, \"string_matches\");\n          break;\n        case STRING_STARTS_WITH:\n          ctx.register(sym, \"string_starts_with\");\n          break;\n        case TO_STRING:\n          ctx.register(sym, \"to_string\");\n          break;\n        case fp32ToFp64:\n          ctx.register(sym, \"__conv<float, double>\");\n          break;\n        case fp32ToI32:\n          ctx.register(sym, \"__conv<float, int32_t>\");\n          break;\n        case fp32ToI64:\n          ctx.register(sym, \"__conv<float, int64_t>\");\n          break;\n        case fp64ToFp32:\n          ctx.register(sym, \"__conv<double, float>\");\n          break;\n        case fp64ToI32:\n          ctx.register(sym, \"__conv<double, int32_t>\");\n          break;\n        case fp64ToI64:\n          ctx.register(sym, \"__conv<double, int64_t>\");\n          break;\n        case i32ToFp32:\n          ctx.register(sym, \"__conv<int32_t, float>\");\n          break;\n        case i32ToFp64:\n          ctx.register(sym, \"__conv<int32_t, double>\");\n          break;\n        case i32ToI64:\n          ctx.register(sym, \"__conv<int32_t, int64_t>\");\n          break;\n        case i64ToFp32:\n          ctx.register(sym, \"__conv<int64_t, float>\");\n          break;\n        case i64ToFp64:\n          ctx.register(sym, \"__conv<int64_t, double>\");\n          break;\n        case i64ToI32:\n          ctx.register(sym, \"__conv<int64_t, int32_t>\");\n          break;\n        case CHAR_AT:\n          ctx.register(sym, \"char_at\");\n          break;\n        case STRING_LENGTH:\n          ctx.register(sym, \"string_length\");\n          break;\n        case STRING_TO_LIST:\n          ctx.register(sym, \"string_to_list\");\n          break;\n        case LIST_TO_STRING:\n          ctx.register(sym, \"list_to_string\");\n          break;\n        case SUBSTRING:\n          ctx.register(sym, \"substring\");\n          break;\n        case stringToI32:\n          ctx.register(sym, \"string_to_i32\");\n          break;\n        case stringToI64:\n          ctx.register(sym, \"string_to_i64\");\n          break;\n        case OPAQUE_SET_EMPTY:\n          ctx.register(sym, \"opaque_set_empty\");\n          break;\n        case OPAQUE_SET_PLUS:\n          ctx.register(sym, \"opaque_set_plus\");\n          break;\n        case OPAQUE_SET_MINUS:\n          ctx.register(sym, \"opaque_set_minus\");\n          break;\n        case OPAQUE_SET_UNION:\n          ctx.register(sym, \"opaque_set_union\");\n          break;\n        case OPAQUE_SET_DIFF:\n          ctx.register(sym, \"opaque_set_diff\");\n          break;\n        case OPAQUE_SET_CHOOSE:\n          ctx.register(sym, \"opaque_set_choose\");\n          break;\n        case OPAQUE_SET_SIZE:\n          ctx.register(sym, \"opaque_set_size\");\n          break;\n        case OPAQUE_SET_MEMBER:\n          ctx.register(sym, \"opaque_set_member\");\n          break;\n        case OPAQUE_SET_SINGLETON:\n          ctx.register(sym, \"opaque_set_singleton\");\n          break;\n        case OPAQUE_SET_SUBSET:\n          ctx.register(sym, \"opaque_set_subset\");\n          break;\n        case OPAQUE_SET_FROM_LIST:\n          ctx.register(sym, \"opaque_set_from_list\");\n          break;\n        case IS_SET_SAT:\n          ctx.register(sym, \"is_set_sat\");\n          break;\n        default:\n          throw new AssertionError(\"unhandled built-in function symbol: \" + sym);\n      }\n    }\n\n    private void makeDefinitions() {\n      var sorted =\n          userDefinedFunctions.stream()\n              .sorted(Comparator.comparing(Object::toString))\n              .collect(Collectors.toList());\n      for (FunctionSymbol sym : sorted) {\n        makeDefinitionForUserDefinedFunc(sym);\n      }\n      for (PredicateFunctionSymbol funcSym : predFunctions) {\n        makeDefinitionForPredFunc(funcSym);\n      }\n    }\n\n    private void makeDefinitionForUserDefinedFunc(FunctionSymbol sym) {\n      UserFunctionDef def = (UserFunctionDef) ctx.getProgram().getDef(sym);\n      out.println();\n      out.print(\"term_ptr \" + ctx.lookupRepr(sym) + \"(\");\n      Map<Var, CppExpr> env = new HashMap<>();\n      Iterator<Var> params = def.getParams().iterator();\n      int n = sym.getArity();\n      List<String> cppParams = new ArrayList<>();\n      for (int i = 0; i < n; ++i) {\n        String id = ctx.newId(\"x\");\n        CppVar var = CppVar.mk(id);\n        env.put(params.next(), var);\n        out.print(\"term_ptr \");\n        var.print(out);\n        if (i < n - 1) {\n          out.print(\", \");\n        }\n        cppParams.add(var.toString());\n      }\n      out.println(\") {\");\n      if (n == 0) {\n        out.println(\"  static std::atomic<term_ptr> memo{nullptr};\");\n        out.println(\"  if (memo) { return memo; }\");\n      } else {\n        if (n == 1) {\n          out.println(\"  typedef term_ptr Key;\");\n          out.println(\"  Key key = \" + cppParams.get(0) + \";\");\n        } else {\n          out.println(\"  typedef std::array<term_ptr, \" + n + \"> Key;\");\n          out.println(\"  Key key = {\" + String.join(\", \", cppParams) + \"};\");\n        }\n        out.println(\"  static ConcurrentHashMap<Key, term_ptr, boost::hash<Key>> memo;\");\n        out.println(\"  auto it = memo.find(key);\");\n        out.println(\"  if (it != memo.end()) { return it->second; }\");\n      }\n      Pair<CppStmt, CppExpr> p = tcg.gen(def.getBody(), env);\n      p.fst().println(out, 1);\n      String retVar = ctx.newId(\"ret\");\n      CppDecl.mk(retVar, p.snd()).println(out, 1);\n      if (n == 0) {\n        out.println(\"  memo = \" + retVar + \";\");\n      } else {\n        out.println(\"  memo.emplace(key, \" + retVar + \");\");\n      }\n      CppReturn.mk(CppVar.mk(retVar)).println(out, 1);\n      out.println(\"}\");\n    }\n\n    private void makeDefinitionForPredFunc(PredicateFunctionSymbol funcSym) {\n      out.println();\n      out.print(\"term_ptr \" + ctx.lookupRepr(funcSym) + \"(\");\n      RelationSymbol predSym = funcSym.getPredicateSymbol();\n      int n = predSym.getArity();\n      BindingType[] bindings = funcSym.getBindings();\n      int j = 0;\n      int nbound = numBound(bindings);\n      List<Integer> free = new ArrayList<>();\n      boolean hasIgnored = false;\n      CppVar[] args = new CppVar[bindings.length];\n      for (int i = 0; i < n; ++i) {\n        if (bindings[i].isBound()) {\n          String id = ctx.newId(\"x\");\n          CppVar var = CppVar.mk(id);\n          args[i] = var;\n          out.print(\"term_ptr \");\n          var.print(out);\n          if (j < nbound - 1) {\n            out.print(\", \");\n          }\n          j++;\n        } else if (bindings[i].isFree()) {\n          free.add(i);\n        } else if (bindings[i].isIgnored()) {\n          hasIgnored = true;\n        }\n      }\n      if (hasIgnored) {\n        System.err.println(\n            \"WARNING: relation query \"\n                + funcSym\n                + \" has ignored attribute; this might lead to slow performance\");\n      }\n      out.println(\") \");\n      Pair<CppStmt, CppExpr> p;\n      if (free.isEmpty()) {\n        p = genRelationContains(predSym, args, hasIgnored);\n      } else if (free.size() == 1) {\n        p = genRelationAggMono(predSym, args, free.get(0));\n      } else {\n        p = genRelationAggPoly(predSym, args, free);\n      }\n      CppStmt ret = CppReturn.mk(p.snd());\n      CppBlock.mk(CppSeq.mk(p.fst(), ret)).println(out, 0);\n    }\n\n    private CppExpr mkArgsVec(CppVar[] args) {\n      var cppArgs =\n          Arrays.stream(args)\n              .map(x -> x == null ? CppNullptr.INSTANCE : x)\n              .collect(Collectors.toList());\n      return CppVectorLiteral.mk(cppArgs);\n    }\n\n    private Pair<CppStmt, CppExpr> genRelationAggMono(\n        RelationSymbol predSym, CppVar[] args, int pos) {\n      CppExpr name = CppConst.mkString(ctx.lookupRepr(predSym));\n      CppExpr vec = mkArgsVec(args);\n      CppExpr call = CppFuncCall.mk(\"_relation_agg_mono\", name, vec, CppConst.mkInt(pos));\n      return new Pair<>(CppSeq.skip(), call);\n    }\n\n    private Pair<CppStmt, CppExpr> genRelationAggPoly(\n        RelationSymbol predSym, CppVar[] args, List<Integer> free) {\n      CppExpr name = CppConst.mkString(ctx.lookupRepr(predSym));\n      CppExpr vec = mkArgsVec(args);\n      List<CppExpr> projection = new ArrayList<>();\n      int pos = 0;\n      for (int i = 0; i < predSym.getArity(); ++i) {\n        if (i == free.get(pos)) {\n          projection.add(CppConst.mkTrue());\n          pos++;\n        } else {\n          projection.add(CppConst.mkFalse());\n        }\n      }\n      assert pos == free.size();\n      ConstructorSymbol tupleSym = GlobalSymbolManager.lookupTupleSymbol(free.size());\n      String funcName = \"_relation_agg_poly<\" + ctx.lookupRepr(tupleSym) + \">\";\n      CppExpr call = CppFuncCall.mk(funcName, name, vec, CppVectorLiteral.mk(projection));\n      return new Pair<>(CppSeq.skip(), call);\n    }\n\n    private Pair<CppStmt, CppExpr> genRelationContains(\n        RelationSymbol predSym, CppVar[] args, boolean hasIgnored) {\n      CppExpr name = CppConst.mkString(ctx.lookupRepr(predSym));\n      CppExpr vec = mkArgsVec(args);\n      String func;\n      if (hasIgnored) {\n        func = \"_relation_contains\";\n      } else {\n        func = \"_relation_contains_complete\";\n      }\n      CppExpr call = CppFuncCall.mk(func, name, vec);\n      return new Pair<>(CppSeq.skip(), call);\n    }\n\n    private int numBound(BindingType[] bindings) {\n      int n = 0;\n      for (BindingType binding : bindings) {\n        if (binding.isBound()) {\n          n++;\n        }\n      }\n      return n;\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/FunctorCodeGen.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2022-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen;\n\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.ast.Var;\nimport edu.harvard.seas.pl.formulog.codegen.ast.cpp.*;\nimport edu.harvard.seas.pl.formulog.codegen.ast.souffle.SDestructorBody;\nimport edu.harvard.seas.pl.formulog.codegen.ast.souffle.SExprBody;\nimport edu.harvard.seas.pl.formulog.codegen.ast.souffle.SFunctorBody;\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport java.io.*;\nimport java.util.*;\n\npublic class FunctorCodeGen {\n\n  private final CodeGenContext ctx;\n\n  private static final String headerName = \"functors.h\";\n  private static final String sourceName = \"functors.cpp\";\n\n  public FunctorCodeGen(CodeGenContext ctx) {\n    this.ctx = ctx;\n  }\n\n  public void emitFunctors(File directory) throws CodeGenException {\n    emitHeader(directory);\n    emitSource(directory);\n  }\n\n  private void emitHeader(File directory) throws CodeGenException {\n    try (InputStream is = CodeGenUtil.inputSrcFile(headerName)) {\n      assert is != null;\n      File outHeader = CodeGenUtil.outputSrcFile(directory, headerName);\n      try (InputStreamReader isr = new InputStreamReader(is);\n          BufferedReader br = new BufferedReader(isr);\n          PrintWriter out = new PrintWriter(outHeader)) {\n        CodeGenUtil.copyOver(br, out, 0);\n        for (Pair<String, SFunctorBody> p : ctx.getFunctors()) {\n          emitSignature(p.fst(), p.snd(), out);\n          out.println(\";\");\n        }\n        CodeGenUtil.copyOver(br, out, -1);\n      }\n    } catch (IOException e) {\n      throw new CodeGenException(e);\n    }\n  }\n\n  private Map<Var, CppVar> emitSignature(String functor, SFunctorBody body, PrintWriter out) {\n    out.print(\"souffle::RamDomain \");\n    out.print(functor);\n    out.print(\"(\");\n    if (body.isStateful()) {\n      out.print(\"souffle::SymbolTable *st, souffle::RecordTable *rt\");\n      if (body.getArity() > 0) {\n        out.print(\", \");\n      }\n    }\n    List<Var> flgArgs = body.getArgs();\n    Map<Var, CppVar> m = new LinkedHashMap<>();\n    for (int i = 0; i < body.getArity(); ++i) {\n      out.print(\"souffle::RamDomain \");\n      String arg = \"arg\" + i;\n      m.put(flgArgs.get(i), CppVar.mk(arg));\n      out.print(arg);\n      if (i < body.getArity() - 1) {\n        out.print(\", \");\n      }\n    }\n    out.print(\")\");\n    return m;\n  }\n\n  private void emitSource(File directory) throws CodeGenException {\n    try (InputStream is = CodeGenUtil.inputSrcFile(sourceName)) {\n      assert is != null;\n      File outSource = CodeGenUtil.outputSrcFile(directory, sourceName);\n      try (InputStreamReader isr = new InputStreamReader(is);\n          BufferedReader br = new BufferedReader(isr);\n          PrintWriter out = new PrintWriter(outSource)) {\n        CodeGenUtil.copyOver(br, out, 0);\n        new SourceWorker(out).emitFunctors();\n        CodeGenUtil.copyOver(br, out, -1);\n      }\n    } catch (IOException e) {\n      throw new CodeGenException(e);\n    }\n  }\n\n  private class SourceWorker {\n\n    private final PrintWriter out;\n\n    public SourceWorker(PrintWriter out_) {\n      out = out_;\n    }\n\n    public void emitFunctors() {\n      for (Pair<String, SFunctorBody> p : ctx.getFunctors()) {\n        emitFunctor(p.fst(), p.snd());\n        out.println();\n      }\n    }\n\n    private void emitFunctor(String functor, SFunctorBody body) {\n      Map<Var, CppVar> params = emitSignature(functor, body, out);\n      out.print(\" \");\n      Pair<CppStmt, Map<Var, CppExpr>> p1 = unpackParams(params);\n      Pair<CppStmt, CppExpr> p2;\n      if (body instanceof SExprBody) {\n        p2 = genTermBody(((SExprBody) body).getBody(), p1.snd());\n      } else {\n        assert body instanceof SDestructorBody;\n        p2 = genDtorBody((SDestructorBody) body, p1.snd());\n      }\n      CppStmt ret = CppReturn.mk(p2.snd());\n      CppStmt block = CppBlock.mk(CppSeq.mk(p1.fst(), p2.fst(), ret));\n      block.println(out, 0);\n    }\n\n    private Pair<CppStmt, Map<Var, CppExpr>> unpackParams(Map<Var, CppVar> params) {\n      List<CppStmt> stmts = new ArrayList<>();\n      Map<Var, CppExpr> env = new HashMap<>();\n      for (Map.Entry<Var, CppVar> e : params.entrySet()) {\n        String x = ctx.newId(\"x\");\n        CppExpr call = TermCodeGen.genUnintizeTerm(e.getValue());\n        CppStmt stmt = CppDecl.mk(x, call);\n        stmts.add(stmt);\n        env.put(e.getKey(), CppVar.mk(x));\n      }\n      return new Pair<>(CppSeq.mk(stmts), env);\n    }\n\n    private Pair<CppStmt, CppExpr> genTermBody(Term t, Map<Var, CppExpr> env) {\n      TermCodeGen tcg = new TermCodeGen(ctx);\n      Pair<CppStmt, CppExpr> p = tcg.gen(t, env);\n      CppExpr pack = TermCodeGen.genIntizeTerm(p.snd());\n      return new Pair<>(p.fst(), pack);\n    }\n\n    private Pair<CppStmt, CppExpr> genDtorBody(SDestructorBody body, Map<Var, CppExpr> env) {\n      Pair<CppStmt, CppExpr> p = new TermCodeGen(ctx).gen(body.getScrutinee(), env);\n      String func = \"dtor<\" + ctx.lookupRepr(body.getSymbol()) + \">\";\n      CppExpr call = CppFuncCall.mk(func, p.snd());\n      return new Pair<>(p.fst(), call);\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/MainCpp.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2020-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen;\n\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.codegen.ast.cpp.*;\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport edu.harvard.seas.pl.formulog.smt.PushPopSolver;\nimport edu.harvard.seas.pl.formulog.symbols.RelationSymbol;\nimport edu.harvard.seas.pl.formulog.unification.SimpleSubstitution;\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.util.*;\n\npublic class MainCpp extends TemplateSrcFile {\n\n  public MainCpp(CodeGenContext ctx) {\n    super(\"main.cpp\", ctx);\n  }\n\n  public void gen(BufferedReader br, PrintWriter out) throws IOException, CodeGenException {\n    Worker pr = new Worker(out);\n    CodeGenUtil.copyOver(br, out, 0);\n    pr.loadExternalEdbs();\n    CodeGenUtil.copyOver(br, out, 1);\n    pr.loadStaticFacts();\n    CodeGenUtil.copyOver(br, out, 2);\n    pr.printIdbsToDisk();\n    CodeGenUtil.copyOver(br, out, -1);\n  }\n\n  private class Worker {\n\n    private final PrintWriter out;\n\n    public Worker(PrintWriter out) {\n      this.out = out;\n    }\n\n    public void loadExternalEdbs() {\n      for (RelationSymbol sym : ctx.getProgram().getFactSymbols()) {\n        if (sym.isDisk()) {\n          loadExternalEdbs(sym);\n        }\n      }\n    }\n\n    public void loadExternalEdbs(RelationSymbol sym) {\n      String func = \"loadEdbs\";\n      CppExpr file = CppConst.mkString(sym + \".tsv\");\n      CppExpr repr = CppConst.mkString(ctx.lookupRepr(sym));\n      CppExpr rel = CppMethodCall.mkThruPtr(CppVar.mk(\"globals::program\"), \"getRelation\", repr);\n      CppExpr call = CppFuncCall.mk(func, CppVar.mk(\"dir\"), file, rel);\n      call.toStmt().println(out, 1);\n    }\n\n    public void loadStaticFacts() throws CodeGenException {\n      var prog = ctx.getProgram();\n      prog.getFunctionCallFactory().getDefManager().loadBuiltInFunctions(new PushPopSolver());\n      for (RelationSymbol sym : prog.getFactSymbols()) {\n        for (Term[] tup : prog.getFacts(sym)) {\n          loadStaticFact(sym, tup);\n        }\n      }\n    }\n\n    public void loadStaticFact(RelationSymbol sym, Term[] tup) throws CodeGenException {\n      List<CppExpr> args = new ArrayList<>();\n      List<CppStmt> stmts = new ArrayList<>();\n      TermCodeGen tcg = new TermCodeGen(ctx);\n      for (Term t : tup) {\n        try {\n          t = t.normalize(new SimpleSubstitution());\n        } catch (EvaluationException e) {\n          throw new CodeGenException(\"Could not normalize term occurring in fact: \" + t);\n        }\n        Pair<CppStmt, CppExpr> p = tcg.gen(t, Collections.emptyMap());\n        stmts.add(p.fst());\n        args.add(p.snd());\n      }\n      CppExpr rel = CppConst.mkString(ctx.lookupRepr(sym));\n      stmts.add(CppFuncCall.mk(\"loadFact\", rel, CppVectorLiteral.mk(args)).toStmt());\n      CppSeq.mk(stmts).println(out, 1);\n    }\n\n    public void printIdbsToDisk() {\n      for (RelationSymbol sym : ctx.getProgram().getRuleSymbols()) {\n        if (sym.isDisk()) {\n          out.println(\"    saveToDisk(\\\"\" + ctx.lookupRepr(sym) + \"\\\");\");\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/MatchCodeGen.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2020-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen;\n\nimport edu.harvard.seas.pl.formulog.ast.*;\nimport edu.harvard.seas.pl.formulog.ast.Terms.TermVisitor;\nimport edu.harvard.seas.pl.formulog.codegen.PatternMatchTree.BaseSymbolicTerm;\nimport edu.harvard.seas.pl.formulog.codegen.PatternMatchTree.CtorEdge;\nimport edu.harvard.seas.pl.formulog.codegen.PatternMatchTree.DerivedSymbolicTerm;\nimport edu.harvard.seas.pl.formulog.codegen.PatternMatchTree.Edge;\nimport edu.harvard.seas.pl.formulog.codegen.PatternMatchTree.EdgeVisitor;\nimport edu.harvard.seas.pl.formulog.codegen.PatternMatchTree.InternalNode;\nimport edu.harvard.seas.pl.formulog.codegen.PatternMatchTree.Leaf;\nimport edu.harvard.seas.pl.formulog.codegen.PatternMatchTree.Node;\nimport edu.harvard.seas.pl.formulog.codegen.PatternMatchTree.NodeVisitor;\nimport edu.harvard.seas.pl.formulog.codegen.PatternMatchTree.PrimEdge;\nimport edu.harvard.seas.pl.formulog.codegen.PatternMatchTree.SymbolicTerm;\nimport edu.harvard.seas.pl.formulog.codegen.PatternMatchTree.VarEdge;\nimport edu.harvard.seas.pl.formulog.codegen.ast.cpp.*;\nimport edu.harvard.seas.pl.formulog.symbols.BuiltInFunctionSymbol;\nimport edu.harvard.seas.pl.formulog.unification.SimpleSubstitution;\nimport edu.harvard.seas.pl.formulog.unification.Substitution;\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport java.io.PrintWriter;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport org.pcollections.HashPMap;\nimport org.pcollections.HashTreePMap;\n\npublic class MatchCodeGen {\n\n  private final CodeGenContext ctx;\n  private final TermCodeGen tcg;\n\n  public MatchCodeGen(CodeGenContext ctx) {\n    this.ctx = ctx;\n    tcg = new TermCodeGen(ctx);\n  }\n\n  /**\n   * Generate C++ code computing a match expression.\n   *\n   * @param match The match expression\n   * @param env The current variable environment\n   * @return A pair of the C++ code for the match expression and an expression representing the\n   *     result of the match\n   */\n  public Pair<CppStmt, CppExpr> gen(MatchExpr match, Map<Var, CppExpr> env) {\n    return new Worker(new HashMap<>(env)).go(match);\n  }\n\n  /**\n   * This class actually does all the work of generating the C++ code. It has a few \"globals\" that\n   * are available throughout the pattern-matching computation: a variable {@link #res res} to store\n   * the result of the pattern matching computation, and a label {@link #end end} to jump to after\n   * the pattern matching computation is complete.\n   *\n   * <p>It essentially generates a bunch of nested if statements; the innermost code jumps to {@link\n   * #end end} after assigning to {@link #res res}. Backtracking is implemented by falling through\n   * to the next case.\n   */\n  private class Worker {\n\n    private final Map<Var, CppExpr> env;\n    private final List<CppStmt> acc = new ArrayList<>();\n\n    /** A variable to store the result of the pattern match in. */\n    private final String res = ctx.newId(\"res\");\n\n    /** A label to jump to once the match expression has been computed. */\n    private final String end = ctx.newId(\"end\");\n\n    public Worker(Map<Var, CppExpr> env) {\n      this.env = env;\n    }\n\n    /**\n     * Generate the C++ code for a match expression.\n     *\n     * @param match The match expression\n     * @return A pair of the code and an expression holding the result of the match computation\n     */\n    public Pair<CppStmt, CppExpr> go(MatchExpr match) {\n      var p = optimizeIfExpr(match);\n      if (p != null) {\n        return p;\n      }\n      acc.add(CppCtor.mk(\"term_ptr\", res));\n      p = tcg.gen(match.getMatchee(), env);\n      acc.add(p.fst());\n      String scrutineeVar = ctx.newId(\"scrutinee\");\n      acc.add(CppDecl.mk(scrutineeVar, p.snd()));\n      CppExpr scrutinee = CppVar.mk(scrutineeVar);\n      List<Pair<Term, Term>> clauses = preprocess(match.getClauses());\n      PatternMatchTree tree = new PatternMatchTree(clauses);\n      acc.add(processTree(scrutinee, tree));\n      /* Generate some code that dies if no pattern has been matched. */\n      acc.add(\n          new CppStmt() {\n\n            @Override\n            public void println(PrintWriter out, int indent) {\n              CodeGenUtil.printIndent(out, indent);\n              out.print(\"cerr << \\\"No matching case for term: \\\" << \");\n              CppUnop.mkDeref(scrutinee).print(out);\n              out.println(\" << endl;\");\n              CodeGenUtil.printIndent(out, indent);\n              out.println(\"cerr << \");\n              out.println(\"R\\\"_(\" + match + \")_\\\" << endl;\");\n              CppFuncCall.mk(\"abort\").toStmt().println(out, indent);\n            }\n          });\n      acc.add(CppLabel.mk(end));\n      return new Pair<>(CppSeq.mk(acc), CppVar.mk(res));\n    }\n\n    private Pair<CppStmt, CppExpr> optimizeIfExpr(MatchExpr match) {\n      var scrutinee = match.getMatchee();\n      if (!(scrutinee instanceof FunctionCallFactory.FunctionCall)) {\n        return null;\n      }\n      var call = (FunctionCallFactory.FunctionCall) scrutinee;\n      var sym = call.getSymbol();\n      if (sym != BuiltInFunctionSymbol.BEQ && sym != BuiltInFunctionSymbol.BNEQ) {\n        return null;\n      }\n      var clauses = match.getClauses();\n      if (clauses.size() != 2) {\n        return null;\n      }\n      if (clauses.get(0).getLhs() != BoolTerm.mkTrue()\n          || clauses.get(1).getLhs() != BoolTerm.mkFalse()) {\n        return null;\n      }\n      var args = call.getArgs();\n      return optimizeIfExpr(\n          sym == BuiltInFunctionSymbol.BEQ,\n          args[0],\n          args[1],\n          clauses.get(0).getRhs(),\n          clauses.get(1).getRhs());\n    }\n\n    private Pair<CppStmt, CppExpr> optimizeIfExpr(\n        boolean equals, Term t1, Term t2, Term ifTrue, Term ifFalse) {\n      List<CppStmt> stmts = new ArrayList<>();\n      var p1 = tcg.gen(t1, env);\n      var p2 = tcg.gen(t2, env);\n      stmts.add(p1.fst());\n      stmts.add(p2.fst());\n      String res = ctx.newId(\"res\");\n      stmts.add(CppCtor.mk(\"term_ptr\", res));\n      CppExpr cond;\n      if (equals) {\n        cond = CppBinop.mkEq(p1.snd(), p2.snd());\n      } else {\n        cond = CppBinop.mkNotEq(p1.snd(), p2.snd());\n      }\n      p1 = tcg.gen(ifTrue, env);\n      p2 = tcg.gen(ifFalse, env);\n      CppExpr resVar = CppVar.mk(res);\n      CppStmt trueBranch = CppSeq.mk(p1.fst(), CppBinop.mkAssign(resVar, p1.snd()).toStmt());\n      CppStmt falseBranch = CppSeq.mk(p2.fst(), CppBinop.mkAssign(resVar, p2.snd()).toStmt());\n      stmts.add(CppIf.mk(cond, trueBranch, falseBranch));\n      return new Pair<>(CppSeq.mk(stmts), resVar);\n    }\n\n    /**\n     * Preprocess a list of match clauses, i.e., (pattern => expression) pairs.\n     *\n     * @param clauses The match clauses\n     * @return The updated (pattern => expression) pairs\n     */\n    private List<Pair<Term, Term>> preprocess(List<MatchClause> clauses) {\n      List<Pair<Term, Term>> l = new ArrayList<>();\n      Substitution s = new SimpleSubstitution();\n      Set<Var> vars = new HashSet<>();\n      /*\n       * Rename variables in patterns, to ensure that patterns do not share variables\n       * with each other.\n       */\n      for (MatchClause clause : clauses) {\n        Pair<Term, Set<Var>> p = renameVars(clause.getLhs(), s);\n        Term rhs = clause.getRhs().applySubstitution(s);\n        l.add(new Pair<>(p.fst(), rhs));\n        vars.addAll(p.snd());\n      }\n      /*\n       * Declare all the variables used in patterns.\n       */\n      for (Var x : vars) {\n        String id = ctx.newId(\"y\");\n        CppVar cppX = CppVar.mk(id);\n        env.put(x, cppX);\n        acc.add(CppCtor.mk(\"term_ptr\", id));\n      }\n      return l;\n    }\n\n    /**\n     * Rename the variables in a term.\n     *\n     * @param t The term\n     * @param s A substitution to update with bindings from old variables to new ones\n     * @return A pair of the new term and the set of variables in that term\n     */\n    private Pair<Term, Set<Var>> renameVars(Term t, Substitution s) {\n      Set<Var> seen = new HashSet<>();\n      Set<Var> newVars = new HashSet<>();\n      Term newT =\n          t.accept(\n              new TermVisitor<Void, Term>() {\n\n                @Override\n                public Term visit(Var old, Void in) {\n                  assert seen.add(old)\n                      : \"Cannot handle patterns with multiple uses of the same variable: \" + t;\n                  Var renamed = Var.fresh();\n                  newVars.add(renamed);\n                  s.put(old, renamed);\n                  return renamed;\n                }\n\n                @Override\n                public Term visit(Constructor c, Void in) {\n                  Term[] args = c.getArgs();\n                  Term[] newArgs = new Term[args.length];\n                  for (int i = 0; i < args.length; ++i) {\n                    newArgs[i] = args[i].accept(this, in);\n                  }\n                  return c.copyWithNewArgs(newArgs);\n                }\n\n                @Override\n                public Term visit(Primitive<?> p, Void in) {\n                  return p;\n                }\n\n                @Override\n                public Term visit(Expr e, Void in) {\n                  throw new AssertionError(\"impossible\");\n                }\n              },\n              null);\n      return new Pair<>(newT, newVars);\n    }\n\n    /**\n     * Given a C++ expression representing the scrutinee of a match expression and a\n     * pattern-matching tree encoding the match expression's logic, generate C++ code implementing\n     * the match expression.\n     *\n     * @param scrutinee The scrutinee\n     * @param tree The pattern-matching tree\n     * @return The generated C++ code\n     */\n    private CppStmt processTree(CppExpr scrutinee, PatternMatchTree tree) {\n      return new TreeProcessor(scrutinee, tree).go();\n    }\n\n    /** This class is used to turn a pattern-matching tree into C++ code. */\n    private class TreeProcessor {\n\n      private final CppExpr scrutinee;\n      private final PatternMatchTree tree;\n\n      public TreeProcessor(CppExpr scrutinee, PatternMatchTree tree) {\n        this.scrutinee = scrutinee;\n        this.tree = tree;\n      }\n\n      /**\n       * Generate the C++ code encoding the pattern-matching logic of this object's pattern-matching\n       * tree.\n       *\n       * @return The generated C++ code\n       */\n      public CppStmt go() {\n        return go(tree.getRoot(), HashTreePMap.empty());\n      }\n\n      /**\n       * Generate the C++ code corresponding to a node in the pattern matching tree.\n       *\n       * @param node The node\n       * @return\n       */\n      private CppStmt go(Node node, HashPMap<SymbolicTerm, CppExpr> symMap) {\n        return node.accept(\n            new NodeVisitor<Void, CppStmt>() {\n\n              @Override\n              public CppStmt visit(InternalNode node, Void in) {\n                /*\n                 * You're at an internal node that is associated with a symbolic term (derived\n                 * from the scrutinee). Generate a C++ expression for the symbolic term, and\n                 * then generate code for checking that term against each outgoing edge of that\n                 * node.\n                 */\n                List<CppStmt> stmts = new ArrayList<>();\n                SymbolicTerm symTerm = node.getSymbolicTerm();\n                /* Generate the C++ expression for the symbolic term. */\n                CppExpr expr;\n                if (symTerm == BaseSymbolicTerm.INSTANCE) {\n                  expr = scrutinee;\n                } else {\n                  DerivedSymbolicTerm dst = (DerivedSymbolicTerm) symTerm;\n                  CppExpr base = symMap.get(dst.getBase());\n                  assert base != null;\n                  String id = ctx.newId(\"s\");\n                  stmts.add(CppDecl.mk(id, CodeGenUtil.mkComplexTermLookup(base, dst.getIndex())));\n                  expr = CppVar.mk(id);\n                }\n                assert !(symMap.containsKey(symTerm));\n                var newMap = symMap.plus(symTerm, expr);\n                /* Handle the outgoing edges. */\n                for (Pair<Edge<?>, Node> p : tree.getOutgoingEdges(node)) {\n                  stmts.add(go(expr, p.fst(), p.snd(), newMap));\n                }\n                return CppSeq.mk(stmts);\n              }\n\n              @Override\n              public CppStmt visit(Leaf node, Void in) {\n                /*\n                 * You've reached a leaf, so you've successfully matched the scrutinee against a\n                 * pattern. Evaluate the expression on the right-hand side of that pattern,\n                 * assign it to the result variable, and jump to the end label.\n                 */\n                Pair<CppStmt, CppExpr> p = tcg.gen(node.getTerm(), env);\n                CppStmt assign = CppBinop.mkAssign(CppVar.mk(res), p.snd()).toStmt();\n                CppStmt jump = CppGoto.mk(end);\n                return CppSeq.mk(p.fst(), assign, jump);\n              }\n            },\n            null);\n      }\n\n      /**\n       * Given a C++ expression representing the current sub-scrutinee, generate C++ code\n       * implementing the pattern-matching logic encoded by the given edge and the rest of the tree\n       * it leads to.\n       *\n       * @param expr The sub-scrutinee\n       * @param edge The edge encoding the next step of pattern-matching logic\n       * @param dest The destination of the edge, which leads to subsequent pattern-matching logic\n       * @return The generated C++ code\n       */\n      private CppStmt go(\n          CppExpr expr, Edge<?> edge, Node dest, HashPMap<SymbolicTerm, CppExpr> symMap) {\n        return edge.accept(\n            new EdgeVisitor<Void, CppStmt>() {\n\n              @Override\n              public CppStmt visit(VarEdge e, Void in) {\n                CppExpr x = env.get(e.getLabel());\n                assert x instanceof CppVar;\n                // Here, the enclosing `CppBlock` is necessary so that possible variable\n                // initializations aren't in the outside scope, due to limitations of `goto`\n                return CppBlock.mk(\n                    CppSeq.mk(CppBinop.mkAssign(x, expr).toStmt(), go(dest, symMap)));\n              }\n\n              @Override\n              public CppStmt visit(PrimEdge e, Void in) {\n                Pair<CppStmt, CppExpr> p = tcg.gen(e.getLabel(), env);\n                // Primitive edge should have no statements; otherwise, this could pollute the\n                // enclosing scope and cause a compilation error, since `goto` can't jump over\n                // variable initializations in C++\n                assert CodeGenUtil.toString(p.fst(), 0).isEmpty();\n                CppExpr rhs = p.snd();\n                CppExpr guard = CppBinop.mkEq(expr, rhs);\n                CppStmt body = go(dest, symMap);\n                return CppIf.mk(guard, body);\n              }\n\n              @Override\n              public CppStmt visit(CtorEdge e, Void in) {\n                CppExpr symbol = CppVar.mk(ctx.lookupRepr(e.getLabel()));\n                CppExpr guard = CppBinop.mkEq(CppAccess.mkThruPtr(expr, \"sym\"), symbol);\n                CppStmt body = go(dest, symMap);\n                return CppIf.mk(guard, body);\n              }\n            },\n            null);\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/NopQueryPlanner.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen;\n\nimport edu.harvard.seas.pl.formulog.codegen.ast.souffle.SRule;\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport edu.harvard.seas.pl.formulog.validating.Stratum;\nimport java.util.List;\n\npublic class NopQueryPlanner implements QueryPlanner {\n\n  @Override\n  public List<Pair<Integer, int[]>> genPlan(SRule r, Stratum stratum) {\n    return null;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/PatternMatchTree.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2020-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen;\n\nimport edu.harvard.seas.pl.formulog.ast.Constructor;\nimport edu.harvard.seas.pl.formulog.ast.Expr;\nimport edu.harvard.seas.pl.formulog.ast.Primitive;\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.ast.Terms.TermVisitor;\nimport edu.harvard.seas.pl.formulog.ast.Var;\nimport edu.harvard.seas.pl.formulog.symbols.ConstructorSymbol;\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport java.util.ArrayDeque;\nimport java.util.ArrayList;\nimport java.util.Deque;\nimport java.util.HashMap;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.atomic.AtomicInteger;\n\npublic class PatternMatchTree {\n\n  private final Map<Node, List<Pair<Edge<?>, Node>>> m = new HashMap<>();\n  private final Node root;\n\n  /**\n   * Construct a pattern match tree from a list of (pattern => expression) pairs\n   *\n   * @param clauses The (pattern => expression) pairs\n   */\n  public PatternMatchTree(List<Pair<Term, Term>> clauses) {\n    PatternMatchingComputation k = PatternMatchingComputation.mk(clauses);\n    root = build(k);\n  }\n\n  /**\n   * Return the root of the tree.\n   *\n   * @return The root\n   */\n  public Node getRoot() {\n    return root;\n  }\n\n  /**\n   * Get a map from representing the outgoing edges of the given node. The map's iterator order\n   * matches the priority of the edges (high to low).\n   *\n   * @param node The node\n   * @return Map with the outgoing edges\n   */\n  public Iterable<Pair<Edge<?>, Node>> getOutgoingEdges(Node node) {\n    return m.get(node);\n  }\n\n  @Override\n  public String toString() {\n    String s = \"{\\n\\troot=\" + root + \"\\n\";\n    for (Map.Entry<Node, ?> e : m.entrySet()) {\n      s += \"\\t\" + e.getKey() + \" -> \" + e.getValue() + \"\\n\";\n    }\n    return s + \"}\\n\";\n  }\n\n  /**\n   * Turn a pattern-matching computation into a tree representing that computation.\n   *\n   * @param k The computation\n   * @return return The node at the root of that tree\n   */\n  private Node build(PatternMatchingComputation k) {\n    if (k.isFinished()) {\n      return new Leaf(k.getFinalTerm());\n    }\n    Node node = new InternalNode(k.getCurrentSymbolicTerm());\n    List<Pair<Edge<?>, Node>> outgoing = new ArrayList<>();\n    for (Pair<Edge<?>, PatternMatchingComputation> p : k.stepComputation()) {\n      outgoing.add(new Pair<>(p.fst(), build(p.snd())));\n    }\n    m.put(node, outgoing);\n    return node;\n  }\n\n  /**\n   * This interface represents a term that is being matched upon; it is symbolic, in that at compile\n   * time we might not know its actual concrete value.\n   */\n  public static interface SymbolicTerm {}\n\n  /** This class represents the scrutinee of the match expression. */\n  public static enum BaseSymbolicTerm implements SymbolicTerm {\n    INSTANCE;\n\n    @Override\n    public String toString() {\n      return \"B\";\n    }\n  }\n\n  /**\n   * The class represents a symbolic term resulting from indexing into the arguments of another\n   * symbolic term.\n   */\n  public static class DerivedSymbolicTerm implements SymbolicTerm {\n\n    private final SymbolicTerm base;\n    private final int index;\n\n    private DerivedSymbolicTerm(SymbolicTerm base, int index) {\n      this.base = base;\n      this.index = index;\n    }\n\n    public SymbolicTerm getBase() {\n      return base;\n    }\n\n    public int getIndex() {\n      return index;\n    }\n\n    @Override\n    public String toString() {\n      return base + \"[\" + index + \"]\";\n    }\n  }\n\n  /**\n   * This class represents a pattern-matching computation; i.e., the logic that happens when you try\n   * to match a scrutinee against a sequence of (pattern => expression) pairs.\n   */\n  private static class PatternMatchingComputation {\n\n    /**\n     * This field keeps track of all the symbolic terms that we are trying to match against\n     * patterns.\n     */\n    private final Deque<SymbolicTerm> schema;\n\n    /**\n     * This is an iterable representing all the computation that remains to be done. It consists of\n     * pairs. The first element of the pair is a deque of pattern terms; this deque should be the\n     * same size as the {@link #schema schema} field, and symbolic terms are pairwise matched\n     * against terms in this deque. The second element of the pair is the computation that happens\n     * if a complete pattern is matched (i.e., it is the right-hand side of a (pattern =>\n     * expression) pair).\n     */\n    private final Iterable<Pair<Deque<Term>, Term>> continuation;\n\n    private PatternMatchingComputation(\n        Deque<SymbolicTerm> schema, Iterable<Pair<Deque<Term>, Term>> continuation) {\n      this.schema = schema;\n      this.continuation = continuation;\n    }\n\n    /**\n     * Construct a pattern-matching computation from a list of (pattern => expression) pairs.\n     *\n     * @param clauses The (pattern => expression) pairs\n     * @return the pattern-matching computation\n     */\n    public static PatternMatchingComputation mk(List<Pair<Term, Term>> clauses) {\n      /*\n       * Initially, the schema is just the symbolic term representing the entire\n       * scrutinee, and the continuation represents the ordered (pattern =>\n       * expression) pairs.\n       */\n      Deque<SymbolicTerm> schema = new ArrayDeque<>();\n      schema.add(BaseSymbolicTerm.INSTANCE);\n      List<Pair<Deque<Term>, Term>> continuation = new ArrayList<>();\n      for (Pair<Term, Term> clause : clauses) {\n        ArrayDeque<Term> k = new ArrayDeque<>();\n        k.add(clause.fst());\n        continuation.add(new Pair<>(k, clause.snd()));\n      }\n      return new PatternMatchingComputation(schema, continuation);\n    }\n\n    /**\n     * Return the pattern-matching logic that follows the current logic.\n     *\n     * @return An iterable of pairs of edges and pattern-matching computations. The pairs are in\n     *     order of priority (high to low); the pattern-matching computation for an edge represents\n     *     the computation that follows the logic represented by that edge.\n     */\n    public Iterable<Pair<Edge<?>, PatternMatchingComputation>> stepComputation() {\n      assert !isFinished();\n      /* First, create new edges corresponding to the next step of the computation. */\n      List<Pair<Edge<?>, Set<Pair<Deque<Term>, Term>>>> l = new ArrayList<>();\n      Edge<?> lastEdge = null;\n      Set<Pair<Deque<Term>, Term>> lastSet = null;\n      for (Pair<Deque<Term>, Term> p : continuation) {\n        Deque<Term> d = new ArrayDeque<>(p.fst());\n        Edge<?> edge = step(d);\n        p = new Pair<>(d, p.snd());\n        if (edge.equals(lastEdge)) {\n          lastSet.add(p);\n        } else {\n          lastEdge = edge;\n          lastSet = new LinkedHashSet<>();\n          lastSet.add(p);\n          l.add(new Pair<>(lastEdge, lastSet));\n        }\n      }\n      /*\n       * Second, create new pattern-matching computations representing the rest of the\n       * work to do along each edge.\n       */\n      List<Pair<Edge<?>, PatternMatchingComputation>> l2 = new ArrayList<>();\n      for (Pair<Edge<?>, Set<Pair<Deque<Term>, Term>>> p : l) {\n        Edge<?> edge = p.fst();\n        Deque<SymbolicTerm> newSchema = new ArrayDeque<>(schema);\n        SymbolicTerm base = newSchema.removeFirst();\n        if (edge instanceof CtorEdge) {\n          /*\n           * If we're matching against a constructor symbol, we need to also match the\n           * arguments of the current scrutinee against its arguments.\n           */\n          ConstructorSymbol sym = ((CtorEdge) edge).getLabel();\n          for (int i = sym.getArity() - 1; i >= 0; --i) {\n            newSchema.addFirst(new DerivedSymbolicTerm(base, i));\n          }\n        }\n        PatternMatchingComputation k = new PatternMatchingComputation(newSchema, p.snd());\n        l2.add(new Pair<>(edge, k));\n      }\n      return l2;\n    }\n\n    /**\n     * Create an edge corresponding to the pattern matching logic for the first step in processing a\n     * deque of patterns. The provided deque is also updated.\n     *\n     * @param d The deque of patterns\n     * @return The edge\n     */\n    private static Edge<?> step(Deque<Term> d) {\n      Term next = d.removeFirst();\n      Edge<?> edge = next.accept(tv, null);\n      if (next instanceof Constructor) {\n        /*\n         * If we are matching against a constructor, then we need to recursively match\n         * against its arguments\n         */\n        Constructor ctor = (Constructor) next;\n        Term[] args = ctor.getArgs();\n        for (int i = args.length - 1; i >= 0; --i) {\n          d.addFirst(args[i]);\n        }\n      }\n      return edge;\n    }\n\n    /**\n     * Get the symbolic term that we are currently trying to match against a pattern.\n     *\n     * @return The symbolic term\n     */\n    public SymbolicTerm getCurrentSymbolicTerm() {\n      assert !isFinished();\n      return schema.getFirst();\n    }\n\n    /**\n     * Return true iff there are no more computational steps to take.\n     *\n     * @return True iff there are no more computational steps to take\n     */\n    public boolean isFinished() {\n      return schema.isEmpty();\n    }\n\n    /**\n     * Return the expression to evaluate if the current pattern succeeds.\n     *\n     * @return The expression\n     */\n    public Term getFinalTerm() {\n      assert isFinished();\n      return continuation.iterator().next().snd();\n    }\n\n    @Override\n    public String toString() {\n      return \"PatternMatchingComputation [schema=\"\n          + schema\n          + \", continuation=\"\n          + continuation\n          + \"]\";\n    }\n  }\n\n  /**\n   * This field is used to turn a term into an edge corresponding to the first step of logic\n   * necessary for matching against that term.\n   */\n  private static final TermVisitor<Void, Edge<?>> tv =\n      new TermVisitor<Void, Edge<?>>() {\n\n        @Override\n        public Edge<?> visit(Var x, Void in) {\n          return new VarEdge(x);\n        }\n\n        @Override\n        public Edge<?> visit(Constructor c, Void in) {\n          return new CtorEdge(c.getSymbol());\n        }\n\n        @Override\n        public Edge<?> visit(Primitive<?> p, Void in) {\n          return new PrimEdge(p);\n        }\n\n        @Override\n        public Edge<?> visit(Expr e, Void in) {\n          // Expressions cannot occur in patterns\n          throw new AssertionError(\"impossible\");\n        }\n      };\n\n  /** This interface represents a node in the pattern match tree. */\n  public static interface Node {\n\n    <I, O> O accept(NodeVisitor<I, O> visitor, I in);\n  }\n\n  public static interface NodeVisitor<I, O> {\n\n    O visit(InternalNode node, I in);\n\n    O visit(Leaf node, I in);\n  }\n\n  /** The class represents an internal (i.e., non-leaf) node in the tree. */\n  public static class InternalNode implements Node {\n\n    private static AtomicInteger cnt = new AtomicInteger();\n\n    private final SymbolicTerm symbolicTerm;\n    private final int id = cnt.getAndIncrement();\n\n    public InternalNode(SymbolicTerm symbolicTerm) {\n      this.symbolicTerm = symbolicTerm;\n    }\n\n    public SymbolicTerm getSymbolicTerm() {\n      return symbolicTerm;\n    }\n\n    @Override\n    public <I, O> O accept(NodeVisitor<I, O> visitor, I in) {\n      return visitor.visit(this, in);\n    }\n\n    @Override\n    public String toString() {\n      return \"Node#\" + id + \"(\" + symbolicTerm + \")\";\n    }\n  }\n\n  /**\n   * The class represents a leaf of the tree; it holds the computation on the right-hand side of a\n   * (pattern => expression) pair.\n   */\n  public static class Leaf implements Node {\n\n    private final Term term;\n\n    public Leaf(Term term) {\n      this.term = term;\n    }\n\n    public Term getTerm() {\n      return term;\n    }\n\n    @Override\n    public <I, O> O accept(NodeVisitor<I, O> visitor, I in) {\n      return visitor.visit(this, in);\n    }\n\n    @Override\n    public String toString() {\n      return \"Leaf(\" + term + \")\";\n    }\n  }\n\n  /**\n   * This interface represents an edge in the tree. Edges correspond to pattern matching logic\n   * (i.e., binding a variable, or checking a constant or constructor), and this interface is\n   * parametric in the type of computation that needs to be performed.\n   *\n   * @param <T> The type of the edge\n   */\n  public static interface Edge<T> {\n\n    T getLabel();\n\n    <I, O> O accept(EdgeVisitor<I, O> visitor, I in);\n  }\n\n  public static interface EdgeVisitor<I, O> {\n\n    O visit(VarEdge e, I in);\n\n    O visit(PrimEdge e, I in);\n\n    O visit(CtorEdge e, I in);\n  }\n\n  private abstract static class AbstractEdge<T> implements Edge<T> {\n\n    protected final T label;\n\n    public AbstractEdge(T label) {\n      this.label = label;\n    }\n\n    @Override\n    public T getLabel() {\n      return label;\n    }\n\n    @Override\n    public int hashCode() {\n      final int prime = 31;\n      int result = 1;\n      result = prime * result + ((label == null) ? 0 : label.hashCode());\n      return result;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n      if (this == obj) return true;\n      if (obj == null) return false;\n      if (getClass() != obj.getClass()) return false;\n      @SuppressWarnings(\"rawtypes\")\n      AbstractEdge other = (AbstractEdge) obj;\n      if (label == null) {\n        if (other.label != null) return false;\n      } else if (!label.equals(other.label)) return false;\n      return true;\n    }\n  }\n\n  /** This edge represents pattern matching logic in which a variable is bound. */\n  public static class VarEdge extends AbstractEdge<Var> {\n\n    public VarEdge(Var label) {\n      super(label);\n    }\n\n    @Override\n    public <I, O> O accept(EdgeVisitor<I, O> visitor, I in) {\n      return visitor.visit(this, in);\n    }\n\n    @Override\n    public String toString() {\n      return \"VarEdge(\" + label + \")\";\n    }\n  }\n\n  /** This edge represents pattern matching logic in which a primitive constant is checked. */\n  public static class PrimEdge extends AbstractEdge<Primitive<?>> {\n\n    public PrimEdge(Primitive<?> label) {\n      super(label);\n    }\n\n    @Override\n    public <I, O> O accept(EdgeVisitor<I, O> visitor, I in) {\n      return visitor.visit(this, in);\n    }\n\n    @Override\n    public String toString() {\n      return \"PrimEdge(\" + label + \")\";\n    }\n  }\n\n  /** This edge represents pattern matching logic in which a constructor symbol is checked. */\n  public static class CtorEdge extends AbstractEdge<ConstructorSymbol> {\n\n    public CtorEdge(ConstructorSymbol label) {\n      super(label);\n    }\n\n    @Override\n    public <I, O> O accept(EdgeVisitor<I, O> visitor, I in) {\n      return visitor.visit(this, in);\n    }\n\n    @Override\n    public String toString() {\n      return \"CtorEdge(\" + label + \")\";\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/QueryPlanner.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen;\n\nimport edu.harvard.seas.pl.formulog.codegen.ast.souffle.SRule;\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport edu.harvard.seas.pl.formulog.validating.Stratum;\nimport java.util.List;\n\npublic interface QueryPlanner {\n  List<Pair<Integer, int[]>> genPlan(SRule r, Stratum stratum);\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/RuleTranslator.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2022-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen;\n\nimport edu.harvard.seas.pl.formulog.Configuration;\nimport edu.harvard.seas.pl.formulog.ast.BasicProgram;\nimport edu.harvard.seas.pl.formulog.ast.BasicRule;\nimport edu.harvard.seas.pl.formulog.ast.Constructor;\nimport edu.harvard.seas.pl.formulog.ast.Expr;\nimport edu.harvard.seas.pl.formulog.ast.Primitive;\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.ast.Terms;\nimport edu.harvard.seas.pl.formulog.ast.Var;\nimport edu.harvard.seas.pl.formulog.codegen.ast.souffle.SAtom;\nimport edu.harvard.seas.pl.formulog.codegen.ast.souffle.SDestructorBody;\nimport edu.harvard.seas.pl.formulog.codegen.ast.souffle.SExprBody;\nimport edu.harvard.seas.pl.formulog.codegen.ast.souffle.SFunctorCall;\nimport edu.harvard.seas.pl.formulog.codegen.ast.souffle.SInfixBinaryOpAtom;\nimport edu.harvard.seas.pl.formulog.codegen.ast.souffle.SInt;\nimport edu.harvard.seas.pl.formulog.codegen.ast.souffle.SLit;\nimport edu.harvard.seas.pl.formulog.codegen.ast.souffle.SRule;\nimport edu.harvard.seas.pl.formulog.codegen.ast.souffle.SRuleMode;\nimport edu.harvard.seas.pl.formulog.codegen.ast.souffle.STerm;\nimport edu.harvard.seas.pl.formulog.codegen.ast.souffle.SVar;\nimport edu.harvard.seas.pl.formulog.eval.SmtCallFinder;\nimport edu.harvard.seas.pl.formulog.symbols.RelationSymbol;\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport edu.harvard.seas.pl.formulog.validating.InvalidProgramException;\nimport edu.harvard.seas.pl.formulog.validating.Stratifier;\nimport edu.harvard.seas.pl.formulog.validating.Stratum;\nimport edu.harvard.seas.pl.formulog.validating.ValidRule;\nimport edu.harvard.seas.pl.formulog.validating.ast.Assignment;\nimport edu.harvard.seas.pl.formulog.validating.ast.Check;\nimport edu.harvard.seas.pl.formulog.validating.ast.Destructor;\nimport edu.harvard.seas.pl.formulog.validating.ast.SimpleLiteral;\nimport edu.harvard.seas.pl.formulog.validating.ast.SimpleLiteralVisitor;\nimport edu.harvard.seas.pl.formulog.validating.ast.SimplePredicate;\nimport edu.harvard.seas.pl.formulog.validating.ast.SimpleRule;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\npublic class RuleTranslator {\n\n  private final CodeGenContext ctx;\n  private final QueryPlanner queryPlanner;\n  private static final String checkPred = \"CODEGEN_CHECK\";\n  private static int smtSupRelCount = 0;\n\n  public RuleTranslator(CodeGenContext ctx, QueryPlanner queryPlanner) {\n    this.ctx = ctx;\n    this.queryPlanner = queryPlanner;\n  }\n\n  public Pair<List<SRule>, List<Stratum>> translate(BasicProgram prog) throws CodeGenException {\n    var strats = checkStratification(prog);\n    List<SRule> l = new ArrayList<>();\n    Worker worker = new Worker(prog);\n    for (var stratum : strats) {\n      for (RelationSymbol sym : stratum.getPredicateSyms()) {\n        for (BasicRule r : prog.getRules(sym)) {\n          for (var sr : worker.translate(r)) {\n            sr.setQueryPlan(queryPlanner.genPlan(sr, stratum));\n            l.add(sr);\n          }\n        }\n      }\n    }\n    l.addAll(worker.createProjectionRules());\n    l.add(worker.createCheckRule());\n    return new Pair<>(l, strats);\n  }\n\n  private List<Stratum> checkStratification(BasicProgram prog) throws CodeGenException {\n    Stratifier stratifier = new Stratifier(prog);\n    List<Stratum> strats;\n    try {\n      strats = stratifier.stratify();\n    } catch (InvalidProgramException e) {\n      throw new CodeGenException(e);\n    }\n    for (Stratum strat : strats) {\n      if (strat.hasRecursiveNegationOrAggregation()) {\n        throw new CodeGenException(\"Cannot handle recursive negation or aggregation: \" + strat);\n      }\n    }\n    return strats;\n  }\n\n  private class Worker {\n\n    private final BasicProgram prog;\n    private Map<Var, Integer> varCount;\n    private final Set<Pair<RelationSymbol, List<Boolean>>> projections = new HashSet<>();\n    private final SmtCallFinder scf = new SmtCallFinder();\n\n    public Worker(BasicProgram prog) {\n      this.prog = prog;\n    }\n\n    private List<SRule> translate(BasicRule br) throws CodeGenException {\n      SimpleRule sr;\n      try {\n        ValidRule vr = ValidRule.make(br, (_lit, _vars) -> 1);\n        sr = SimpleRule.make(vr, prog.getFunctionCallFactory());\n        varCount = sr.countVariables();\n      } catch (InvalidProgramException e) {\n        throw new CodeGenException(e);\n      }\n\n      List<List<SLit>> bodies = new ArrayList<>();\n      List<SLit> curBody = new ArrayList<>();\n      List<Set<SVar>> vars = new ArrayList<>();\n      Set<SVar> curVars = new HashSet<>();\n      for (int i = 0; i < sr.getBodySize(); ++i) {\n        var lit = sr.getBody(i);\n        if (Configuration.codegenSplitOnSmt && curBody.size() > 1 && scf.containsSmtCall(lit)) {\n          bodies.add(curBody);\n          curBody = new ArrayList<>();\n          vars.add(curVars);\n          curVars = new HashSet<>(curVars);\n        }\n        var lits = translate(lit);\n        curBody.addAll(lits);\n        addVars(lits, curVars);\n      }\n\n      var head = sr.getHead();\n      if (Configuration.codegenSplitOnSmt && curBody.size() > 1 && scf.containsSmtCall(head)) {\n        bodies.add(curBody);\n        curBody = new ArrayList<>();\n        vars.add(curVars);\n        curVars = new HashSet<>(curVars);\n      }\n      bodies.add(curBody);\n      vars.add(curVars);\n\n      List<SLit> headTrans = translate(head);\n      assert headTrans.size() == 1;\n      var newHead = headTrans.get(0);\n      var liveVars = liveVarsByBody(newHead, bodies);\n\n      List<SRule> rules = new ArrayList<>();\n      SAtom prevSup = null;\n      for (int i = 0; i < bodies.size(); ++i) {\n        var body = bodies.get(i);\n        if (prevSup != null) {\n          body.add(0, prevSup);\n        }\n        if (i < bodies.size() - 1) {\n          var presentVars = vars.get(i);\n          var varsToKeep =\n              presentVars.stream()\n                  .filter(liveVars.get(i + 1)::contains)\n                  .collect(Collectors.toList());\n          var supAtom = newSmtSupAtom(varsToKeep);\n          rules.add(new SRule(supAtom, body));\n          prevSup = supAtom;\n        } else {\n          rules.add(new SRule(newHead, body));\n        }\n      }\n      return rules;\n    }\n\n    void addVars(Collection<SLit> lits, Set<SVar> vars) {\n      for (var lit : lits) {\n        lit.varSet(vars);\n      }\n    }\n\n    List<Set<SVar>> liveVarsByBody(SLit head, List<List<SLit>> bodies) {\n      List<Set<SVar>> l = new ArrayList<>();\n      Set<SVar> vars = head.varSet();\n      l.add(vars);\n      for (var it = bodies.listIterator(bodies.size()); it.hasPrevious(); ) {\n        vars = new HashSet<>(vars);\n        addVars(it.previous(), vars);\n        l.add(vars);\n      }\n      Collections.reverse(l);\n      return l;\n    }\n\n    private SAtom newSmtSupAtom(Collection<SVar> vars) {\n      var name = \"CODEGEN_SMT_SUP_\" + smtSupRelCount++;\n      ctx.registerCustomRelation(name, vars.size(), SRuleMode.OUTPUT);\n      return new SAtom(name, new ArrayList<>(vars), false);\n    }\n\n    private List<SLit> translate(SimpleLiteral lit) {\n      return lit.accept(\n          new SimpleLiteralVisitor<Void, List<SLit>>() {\n            @Override\n            public List<SLit> visit(Assignment assignment, Void input) {\n              STerm lhs = translate(assignment.getDef());\n              STerm rhs = translate(assignment.getVal());\n              return Collections.singletonList(new SInfixBinaryOpAtom(lhs, \"=\", rhs));\n            }\n\n            @Override\n            public List<SLit> visit(Check check, Void input) {\n              STerm lhs = translate(check.getLhs());\n              STerm rhs = translate(check.getRhs());\n              return Collections.singletonList(\n                  new SInfixBinaryOpAtom(lhs, check.isNegated() ? \"!=\" : \"=\", rhs));\n            }\n\n            @Override\n            public List<SLit> visit(Destructor destructor, Void input) {\n              List<SLit> l = new ArrayList<>();\n              Term scrutinee = destructor.getScrutinee();\n              STerm translatedScrutineeExpr = translate(scrutinee);\n              Var scrutineeVar = Var.fresh();\n              STerm translatedScrutineeVar = new SVar(scrutineeVar);\n              l.add(new SInfixBinaryOpAtom(translatedScrutineeVar, \"=\", translatedScrutineeExpr));\n              List<Var> args = Collections.singletonList(scrutineeVar);\n              String functor = ctx.lookupOrCreateFunctorName(destructor.getSymbol());\n              var dtor = new SDestructorBody(args, scrutineeVar, destructor.getSymbol());\n              ctx.registerFunctorBody(functor, dtor);\n              List<STerm> translatedArgs =\n                  args.stream().map(Worker.this::translate).collect(Collectors.toList());\n              assert translatedArgs.size() == 1 && translatedArgs.get(0) instanceof SVar;\n              STerm lhs = new SFunctorCall(functor, translatedArgs);\n              SVar recRef = new SVar(Var.fresh());\n              l.add(new SInfixBinaryOpAtom(lhs, \"=\", recRef));\n              SVar checkOutput = new SVar(Var.fresh());\n              l.add(new SAtom(checkPred, Arrays.asList(recRef, checkOutput), false));\n              int arity = destructor.getSymbol().getArity();\n              Var[] bindings = destructor.getBindings();\n              for (int i = 0; i < arity; ++i) {\n                SFunctorCall call =\n                    new SFunctorCall(\"nth\", new SInt(i), translatedArgs.get(0), checkOutput);\n                l.add(new SInfixBinaryOpAtom(translate(bindings[i]), \"=\", call));\n              }\n              return l;\n            }\n\n            @Override\n            public List<SLit> visit(SimplePredicate predicate, Void input) {\n              List<STerm> args =\n                  Arrays.stream(predicate.getArgs())\n                      .map(Worker.this::translate)\n                      .collect(Collectors.toList());\n              SLit lit;\n              if (predicate.isNegated()) {\n                lit = handleNegatedPredicate(predicate, args);\n              } else {\n                String symbol = ctx.lookupRepr(predicate.getSymbol());\n                lit = new SAtom(symbol, args, false);\n              }\n              return Collections.singletonList(lit);\n            }\n          },\n          null);\n    }\n\n    private SLit handleNegatedPredicate(SimplePredicate predicate, List<STerm> souffleArgs) {\n      List<Boolean> projection = new ArrayList<>();\n      int ignoredCount = 0;\n      var args = predicate.getArgs();\n      for (Term arg : args) {\n        if (arg instanceof Var && varCount.get((Var) arg) == 1) {\n          ignoredCount++;\n          projection.add(false);\n        } else {\n          projection.add(true);\n        }\n      }\n      RelationSymbol sym = predicate.getSymbol();\n      if (ignoredCount == 0) {\n        return new SAtom(ctx.lookupRepr(sym), souffleArgs, true);\n      }\n      projections.add(new Pair<>(predicate.getSymbol(), projection));\n      List<STerm> newArgs = new ArrayList<>();\n      for (int i = 0; i < souffleArgs.size(); ++i) {\n        if (projection.get(i)) {\n          newArgs.add(souffleArgs.get(i));\n        }\n      }\n      return new SAtom(createProjectionSymbol(sym, projection), newArgs, true);\n    }\n\n    private String createProjectionSymbol(RelationSymbol sym, List<Boolean> projection) {\n      StringBuilder sb = new StringBuilder();\n      sb.append(\"CODEGEN_PROJECT_\");\n      sb.append(ctx.lookupRepr(sym));\n      for (Boolean retain : projection) {\n        sb.append(retain ? \"1\" : \"0\");\n      }\n      return sb.toString();\n    }\n\n    private SFunctorCall liftToFunctor(Term t) {\n      String functor = ctx.lookupOrCreateFunctorName(t);\n      List<Var> args = new ArrayList<>(t.varSet());\n      ctx.registerFunctorBody(functor, new SExprBody(args, t));\n      List<STerm> translatedArgs = args.stream().map(this::translate).collect(Collectors.toList());\n      return new SFunctorCall(functor, translatedArgs);\n    }\n\n    private STerm translate(Term t) {\n      return t.accept(\n          new Terms.TermVisitor<Void, STerm>() {\n            @Override\n            public STerm visit(Var t, Void in) {\n              return new SVar(t);\n            }\n\n            @Override\n            public STerm visit(Constructor c, Void in) {\n              return liftToFunctor(c);\n            }\n\n            @Override\n            public STerm visit(Primitive<?> p, Void in) {\n              return liftToFunctor(p);\n            }\n\n            @Override\n            public STerm visit(Expr e, Void in) {\n              return liftToFunctor(e);\n            }\n          },\n          null);\n    }\n\n    List<SRule> createProjectionRules() {\n      List<SRule> l = new ArrayList<>();\n      for (var p : projections) {\n        l.add(createProjection(p.fst(), p.snd()));\n      }\n      return l;\n    }\n\n    private SRule createProjection(RelationSymbol sym, List<Boolean> projection) {\n      List<STerm> headArgs = new ArrayList<>();\n      List<STerm> bodyArgs = new ArrayList<>();\n      for (int i = 0; i < sym.getArity(); ++i) {\n        STerm arg = new SVar(Var.fresh(\"x\" + i));\n        bodyArgs.add(arg);\n        if (projection.get(i)) {\n          headArgs.add(arg);\n        }\n      }\n      String projSym = createProjectionSymbol(sym, projection);\n      SLit head = new SAtom(projSym, headArgs, false);\n      SLit bodyAtom = new SAtom(ctx.lookupRepr(sym), bodyArgs, false);\n      ctx.registerCustomRelation(projSym, headArgs.size(), SRuleMode.INTERMEDIATE);\n      return new SRule(head, bodyAtom);\n    }\n\n    public SRule createCheckRule() {\n      ctx.registerCustomRelation(checkPred, 2, SRuleMode.INTERMEDIATE);\n      SLit head = new SAtom(checkPred, Arrays.asList(new SInt(1), new SInt(1)), false);\n      return new SRule(head);\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/SmtParserCpp.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen;\n\nimport edu.harvard.seas.pl.formulog.codegen.ast.cpp.CppBinop;\nimport edu.harvard.seas.pl.formulog.codegen.ast.cpp.CppConst;\nimport edu.harvard.seas.pl.formulog.codegen.ast.cpp.CppDecl;\nimport edu.harvard.seas.pl.formulog.codegen.ast.cpp.CppExpr;\nimport edu.harvard.seas.pl.formulog.codegen.ast.cpp.CppExprFromString;\nimport edu.harvard.seas.pl.formulog.codegen.ast.cpp.CppFuncCall;\nimport edu.harvard.seas.pl.formulog.codegen.ast.cpp.CppIf;\nimport edu.harvard.seas.pl.formulog.codegen.ast.cpp.CppReturn;\nimport edu.harvard.seas.pl.formulog.codegen.ast.cpp.CppSeq;\nimport edu.harvard.seas.pl.formulog.codegen.ast.cpp.CppStmt;\nimport edu.harvard.seas.pl.formulog.codegen.ast.cpp.CppVar;\nimport edu.harvard.seas.pl.formulog.smt.SmtLibParser;\nimport edu.harvard.seas.pl.formulog.smt.SmtLibParser.SmtLibParseException;\nimport edu.harvard.seas.pl.formulog.symbols.ConstructorSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.parameterized.BuiltInConstructorSymbolBase;\nimport edu.harvard.seas.pl.formulog.symbols.parameterized.ParameterizedConstructorSymbol;\nimport edu.harvard.seas.pl.formulog.types.BuiltInTypes;\nimport edu.harvard.seas.pl.formulog.types.Types.AlgebraicDataType;\nimport edu.harvard.seas.pl.formulog.types.Types.AlgebraicDataType.ConstructorScheme;\nimport edu.harvard.seas.pl.formulog.types.Types.Type;\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\npublic class SmtParserCpp extends TemplateSrcFile {\n\n  public SmtParserCpp(CodeGenContext ctx) {\n    super(\"smt_parser.cpp\", ctx);\n  }\n\n  @Override\n  void gen(BufferedReader br, PrintWriter out) throws IOException, CodeGenException {\n    Worker w = new Worker(out);\n    CodeGenUtil.copyOver(br, out, 0);\n    w.outputShouldRecord();\n    CodeGenUtil.copyOver(br, out, 1);\n    w.outputParseFuncs();\n    CodeGenUtil.copyOver(br, out, 2);\n    w.outputParseTerm();\n    CodeGenUtil.copyOver(br, out, -1);\n  }\n\n  private class Worker {\n\n    private final PrintWriter out;\n    private final Set<ConstructorSymbol> trackedSmtVars = new HashSet<>();\n    private final Map<Type, String> parseFuncs = new HashMap<>();\n    private final Map<String, CppStmt> parseFuncDefs = new HashMap<>();\n\n    public Worker(PrintWriter out) {\n      this.out = out;\n      parseFuncs.put(BuiltInTypes.string, \"parse_string\");\n      parseFuncs.put(BuiltInTypes.i32, \"parse_i32\");\n      parseFuncs.put(BuiltInTypes.i64, \"parse_i64\");\n      parseFuncs.put(BuiltInTypes.fp32, \"parse_fp32\");\n      parseFuncs.put(BuiltInTypes.fp64, \"parse_fp64\");\n      parseFuncs.put(BuiltInTypes.bool, \"parse_bool\");\n    }\n\n    private void outputShouldRecord() throws CodeGenException {\n      for (ConstructorSymbol sym : ctx.getConstructorSymbols()) {\n        var ty = getSmtVarType(sym);\n        if (ty != null) {\n          try {\n            if (SmtLibParser.shouldRecord(ty)) {\n              out.printf(\"    case %s: return true;\\n\", ctx.lookupRepr(sym));\n              trackedSmtVars.add(sym);\n            }\n          } catch (SmtLibParseException e) {\n            throw new CodeGenException(e);\n          }\n        }\n      }\n    }\n\n    private void outputParseFuncs() {\n      for (ConstructorSymbol sym : trackedSmtVars) {\n        genParseFunc(getSmtVarType(sym));\n      }\n      // Forward-declare functions to account for mutually recursive data types.\n      for (String name : parseFuncDefs.keySet()) {\n        out.println(\"struct \" + name + \";\");\n      }\n      out.println();\n      for (Map.Entry<String, CppStmt> e : parseFuncDefs.entrySet()) {\n        out.println(\"struct \" + e.getKey() + \" {\");\n        out.println(\"  term_ptr operator()(SmtLibTokenizer &t) {\");\n        e.getValue().println(out, 2);\n        out.println(\"  }\\n};\\n\");\n      }\n    }\n\n    private String genParseFunc(AlgebraicDataType ty) {\n      String name = parseFuncs.get(ty);\n      if (name != null) {\n        return name;\n      }\n      name = \"parse_\" + ctx.newId(CodeGenUtil.mkName(ty.getSymbol()));\n      var wrapped_name = \"parse_adt<\" + name + \">\";\n      parseFuncs.put(ty, wrapped_name);\n\n      List<CppStmt> body = new ArrayList<>();\n      String scrutinee = \"x\";\n      body.add(CppDecl.mk(scrutinee, CppFuncCall.mk(\"parse_identifier\", CppVar.mk(\"t\"))));\n\n      List<Pair<CppExpr, CppStmt>> cases = new ArrayList<>();\n      for (var cs : ty.getConstructors()) {\n        cases.add(genConstructorCase(CppVar.mk(scrutinee), cs));\n      }\n      body.add(CppIf.mk(cases));\n\n      body.add(CppExprFromString.mk(\"abort()\").toStmt());\n      parseFuncDefs.put(name, CppSeq.mk(body));\n      return wrapped_name;\n    }\n\n    private Pair<CppExpr, CppStmt> genConstructorCase(CppExpr scrutinee, ConstructorScheme cs) {\n      ConstructorSymbol sym = cs.getSymbol();\n      var check = CppBinop.mkEq(scrutinee, CppConst.mkString(sym.toString()));\n      List<CppExpr> args = new ArrayList<>();\n      List<CppStmt> stmts = new ArrayList<>();\n      int i = 0;\n      for (var ty : cs.getTypeArgs()) {\n        String arg = \"a\" + i;\n        CppStmt decl =\n            CppDecl.mk(arg, CppFuncCall.mk(genParseFunc((AlgebraicDataType) ty), CppVar.mk(\"t\")));\n        stmts.add(decl);\n        args.add(CppVar.mk(arg));\n        i++;\n      }\n      CppExpr term = CppFuncCall.mk(\"Term::make<\" + ctx.lookupRepr(sym) + \">\", args);\n      stmts.add(CppReturn.mk(term));\n      return new Pair<>(check, CppSeq.mk(stmts));\n    }\n\n    private void outputParseTerm() {\n      for (ConstructorSymbol sym : trackedSmtVars) {\n        var ty = getSmtVarType(sym);\n        var func = parseFuncs.get(ty);\n        out.printf(\"    case %s: return %s(t);\\n\", ctx.lookupRepr(sym), func);\n      }\n    }\n\n    private AlgebraicDataType getSmtVarType(ConstructorSymbol sym) {\n      if (sym instanceof ParameterizedConstructorSymbol) {\n        var base = ((ParameterizedConstructorSymbol) sym).getBase();\n        if (base.equals(BuiltInConstructorSymbolBase.SMT_VAR)) {\n          return SmtLibParser.stripSymType(\n              (AlgebraicDataType) sym.getCompileTimeType().getRetType());\n        }\n      }\n      return null;\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/SmtShimCpp.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen;\n\nimport edu.harvard.seas.pl.formulog.Configuration;\nimport edu.harvard.seas.pl.formulog.codegen.ast.cpp.*;\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport edu.harvard.seas.pl.formulog.smt.SmtLibShim;\nimport edu.harvard.seas.pl.formulog.symbols.BuiltInConstructorSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.ConstructorSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.ConstructorSymbolType;\nimport edu.harvard.seas.pl.formulog.symbols.parameterized.BuiltInConstructorSymbolBase;\nimport edu.harvard.seas.pl.formulog.symbols.parameterized.ParameterizedConstructorSymbol;\nimport edu.harvard.seas.pl.formulog.types.Types.TypeIndex;\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.util.HashSet;\nimport java.util.Set;\n\npublic class SmtShimCpp extends TemplateSrcFile {\n\n  public SmtShimCpp(CodeGenContext ctx) {\n    super(\"smt_shim.cpp\", ctx);\n  }\n\n  public void gen(BufferedReader br, PrintWriter out) throws IOException {\n    Worker pr = new Worker(out);\n    CodeGenUtil.copyOver(br, out, 0);\n    pr.copyDeclarations();\n    CodeGenUtil.copyOver(br, out, 1);\n    pr.genSolverVarCases();\n    CodeGenUtil.copyOver(br, out, 2);\n    pr.genNeedsTypeAnnotationCases();\n    CodeGenUtil.copyOver(br, out, 3);\n    pr.genSerializationCases();\n    CodeGenUtil.copyOver(br, out, 4);\n    pr.genSymbolSerializationCases();\n    CodeGenUtil.copyOver(br, out, -1);\n  }\n\n  private class Worker {\n\n    private final PrintWriter out;\n    private final Set<ConstructorSymbol> varSymbols = new HashSet<>();\n\n    public Worker(PrintWriter out) {\n      this.out = out;\n      for (ConstructorSymbol sym : ctx.getConstructorSymbols()) {\n        if (isVarSymbol(sym)) {\n          varSymbols.add(sym);\n        }\n      }\n    }\n\n    private boolean isVarSymbol(ConstructorSymbol sym) {\n      return sym instanceof ParameterizedConstructorSymbol\n          && ((ParameterizedConstructorSymbol) sym).getBase()\n              == BuiltInConstructorSymbolBase.SMT_VAR;\n    }\n\n    public void copyDeclarations() {\n      SmtLibShim shim = new SmtLibShim(null, out);\n      shim.initialize(ctx.getProgram(), true);\n      try {\n        shim.setLogic(Configuration.smtLogic);\n        shim.makeDeclarations();\n      } catch (EvaluationException e) {\n        throw new AssertionError(\"impossible\");\n      }\n    }\n\n    public void genSolverVarCases() {\n      if (varSymbols.isEmpty()) {\n        return;\n      }\n      for (ConstructorSymbol sym : varSymbols) {\n        out.println(\"    case \" + ctx.lookupRepr(sym) + \":\");\n      }\n      CppReturn.mk(CppConst.mkTrue()).println(out, 3);\n    }\n\n    public void genSerializationCases() {\n      for (ConstructorSymbol sym : ctx.getConstructorSymbols()) {\n        genSerializationCase(sym);\n      }\n    }\n\n    private void genSerializationCase(ConstructorSymbol sym) {\n      CppStmt stmt = genSerializationCaseBody(sym);\n      if (stmt == null) {\n        return;\n      }\n      out.println(\"    case \" + ctx.lookupRepr(sym) + \": {\");\n      stmt.println(out, 3);\n      out.println(\"      break;\");\n      out.println(\"    }\");\n    }\n\n    private CppStmt genSerializationCaseBody(ConstructorSymbol sym) {\n      if (sym instanceof BuiltInConstructorSymbol) {\n        return genBuiltInConstructorSymbolCase((BuiltInConstructorSymbol) sym);\n      }\n      if (sym instanceof ParameterizedConstructorSymbol) {\n        return genParameterizedConstructorSymbolCase((ParameterizedConstructorSymbol) sym);\n      }\n      return null;\n    }\n\n    private CppStmt genBuiltInConstructorSymbolCase(BuiltInConstructorSymbol sym) {\n      switch (sym) {\n        case ARRAY_CONST:\n          return genSerializeOp(\"const\");\n        case ARRAY_STORE:\n          return genSerializeOp(\"store\");\n        case BV_ADD:\n          return genSerializeOp(\"bvadd\");\n        case BV_AND:\n          return genSerializeOp(\"bvand\");\n        case BV_MUL:\n          return genSerializeOp(\"bvmul\");\n        case BV_NEG:\n          return genSerializeOp(\"bvneg\");\n        case BV_OR:\n          return genSerializeOp(\"bvor\");\n        case BV_SDIV:\n          return genSerializeOp(\"bvsdiv\");\n        case BV_SREM:\n          return genSerializeOp(\"bvsrem\");\n        case BV_SUB:\n          return genSerializeOp(\"bvsub\");\n        case BV_UDIV:\n          return genSerializeOp(\"bvudiv\");\n        case BV_UREM:\n          return genSerializeOp(\"bvurem\");\n        case BV_XOR:\n          return genSerializeOp(\"bvxor\");\n        case BV_SHL:\n          return genSerializeOp(\"bvshl\");\n        case BV_LSHR:\n          return genSerializeOp(\"bvlshr\");\n        case BV_ASHR:\n          return genSerializeOp(\"bvashr\");\n        // Normal constructors don't need special treatment\n        case CMP_EQ:\n        case CMP_GT:\n        case CMP_LT:\n        case CONS:\n        case NIL:\n        case NONE:\n        case SOME:\n          break;\n        case ENTER_FORMULA:\n        case EXIT_FORMULA:\n          return CppFuncCall.mk(\"abort\").toStmt();\n        case FP_ADD:\n          return genSerializeOp(\"fp.add\");\n        case FP_DIV:\n          return genSerializeOp(\"fp.div\");\n        case FP_MUL:\n          return genSerializeOp(\"fp.mul\");\n        case FP_NEG:\n          return genSerializeOp(\"fp.neg\");\n        case FP_REM:\n          return genSerializeOp(\"fp.rem\");\n        case FP_SUB:\n          return genSerializeOp(\"fp.sub\");\n        case INT_ABS:\n          return genSerializeOp(\"abs\");\n        case INT_ADD:\n          return genSerializeOp(\"+\");\n        case INT_BIG_CONST:\n          return genSerializeInt(true);\n        case INT_CONST:\n          return genSerializeInt(false);\n        case INT_DIV:\n          return genSerializeOp(\"div\");\n        case INT_GE:\n          return genSerializeOp(\">=\");\n        case INT_GT:\n          return genSerializeOp(\">\");\n        case INT_LE:\n          return genSerializeOp(\"<=\");\n        case INT_LT:\n          return genSerializeOp(\"<\");\n        case INT_MOD:\n          return genSerializeOp(\"mod\");\n        case INT_MUL:\n          return genSerializeOp(\"*\");\n        case INT_NEG:\n        case INT_SUB:\n          return genSerializeOp(\"-\");\n        case SMT_AND:\n          return genSerializeOp(\"and\");\n        case SMT_EXISTS:\n          return genSerializeQuantifier(true);\n        case SMT_FORALL:\n          return genSerializeQuantifier(false);\n        case SMT_IMP:\n          return genSerializeOp(\"=>\");\n        case SMT_ITE:\n          return genSerializeOp(\"ite\");\n        case SMT_NOT:\n          return genSerializeOp(\"not\");\n        case SMT_OR:\n          return genSerializeOp(\"or\");\n        case STR_AT:\n          return genSerializeOp(\"str.at\");\n        case STR_CONCAT:\n          return genSerializeOp(\"str.++\");\n        case STR_CONTAINS:\n          return genSerializeOp(\"str.contains\");\n        case STR_INDEXOF:\n          return genSerializeOp(\"str.indexof\");\n        case STR_LEN:\n          return genSerializeOp(\"str.len\");\n        case STR_PREFIXOF:\n          return genSerializeOp(\"str.prefixof\");\n        case STR_REPLACE:\n          return genSerializeOp(\"str.replace\");\n        case STR_SUBSTR:\n          return genSerializeOp(\"str.substr\");\n        case STR_SUFFIXOF:\n          return genSerializeOp(\"str.suffixof\");\n      }\n      return null;\n    }\n\n    private int index(ParameterizedConstructorSymbol sym, int i) {\n      return ((TypeIndex) sym.getArgs().get(i).getType()).getIndex();\n    }\n\n    private CppStmt mkCall(String func) {\n      return CppFuncCall.mk(func, CppVar.mk(\"t\")).toStmt();\n    }\n\n    private CppStmt genParameterizedConstructorSymbolCase(ParameterizedConstructorSymbol sym) {\n      switch (sym.getBase()) {\n        case ARRAY_DEFAULT:\n          return genSerializeOp(\"default\");\n        case ARRAY_SELECT:\n          return genSerializeOp(\"select\");\n        case BV_BIG_CONST:\n          return genSerializeBitString(index(sym, 0), true);\n        case BV_CONST:\n          return genSerializeBitString(index(sym, 0), false);\n        case BV_SGE:\n          return genSerializeOp(\"bvsge\");\n        case BV_SGT:\n          return genSerializeOp(\"bvsgt\");\n        case BV_SLE:\n          return genSerializeOp(\"bvsle\");\n        case BV_SLT:\n          return genSerializeOp(\"bvslt\");\n        case BV_TO_BV_SIGNED:\n          return genSerializeBvToBv(index(sym, 0), index(sym, 1), true);\n        case BV_TO_BV_UNSIGNED:\n          return genSerializeBvToBv(index(sym, 0), index(sym, 1), false);\n        case BV_EXTRACT:\n          return mkCall(\"serialize_bv_extract\");\n        case BV_CONCAT:\n          return genSerializeOp(\"concat\");\n        case BV_TO_FP:\n          {\n            int exp = index(sym, 1);\n            int sig = index(sym, 2);\n            String func = \"serialize_bv_to_fp<\" + exp + \", \" + sig + \">\";\n            return mkCall(func);\n          }\n        case BV_UGE:\n          return genSerializeOp(\"bvuge\");\n        case BV_UGT:\n          return genSerializeOp(\"bvugt\");\n        case BV_ULE:\n          return genSerializeOp(\"bvule\");\n        case BV_ULT:\n          return genSerializeOp(\"bvult\");\n        case FP_BIG_CONST:\n          {\n            int exp = index(sym, 0);\n            int sig = index(sym, 1);\n            return genSerializeFp(exp, sig, true);\n          }\n        case FP_CONST:\n          {\n            int exp = index(sym, 0);\n            int sig = index(sym, 1);\n            return genSerializeFp(exp, sig, false);\n          }\n        case FP_EQ:\n          return genSerializeOp(\"fp.eq\");\n        case FP_GE:\n          return genSerializeOp(\"fp.geq\");\n        case FP_GT:\n          return genSerializeOp(\"fp.gt\");\n        case FP_IS_NAN:\n          return genSerializeOp(\"fp.isNaN\");\n        case FP_LE:\n          return genSerializeOp(\"fp.leq\");\n        case FP_LT:\n          return genSerializeOp(\"fp.lt\");\n        case FP_TO_SBV:\n          return genSerializeFpToBv(index(sym, 2), true);\n        case FP_TO_UBV:\n          return genSerializeFpToBv(index(sym, 2), false);\n        case FP_TO_FP:\n          {\n            int exp = index(sym, 2);\n            int sig = index(sym, 3);\n            String func = \"serialize_fp_to_fp<\" + exp + \", \" + sig + \">\";\n            return mkCall(func);\n          }\n        case SMT_EQ:\n          return genSerializeOp(\"=\");\n        case SMT_LET:\n          return mkCall(\"serialize_let\");\n        case SMT_VAR:\n          {\n            CppExpr call = CppFuncCall.mk(\"lookup_var\", CppVar.mk(\"t\"));\n            return CppBinop.mkShiftLeft(CppVar.mk(\"m_in\"), call).toStmt();\n          }\n        case SMT_PAT:\n        case SMT_WRAP_VAR:\n          return CppFuncCall.mk(\"abort\").toStmt();\n        case BV_TO_INT:\n          return genSerializeOp(\"bv2int\");\n        case INT_TO_BV:\n          {\n            int width = index(sym, 0);\n            String func = \"serialize_int2bv<\" + width + \">\";\n            return mkCall(func);\n          }\n      }\n      return null;\n    }\n\n    private CppStmt genSerializeOp(String op) {\n      CppExpr s = CppConst.mkString(op);\n      CppExpr t = CppMethodCall.mkThruPtr(CppVar.mk(\"t\"), \"as_complex\");\n      return CppFuncCall.mk(\"serialize\", s, t).toStmt();\n    }\n\n    private CppStmt genSerializeBitString(int n, boolean big) {\n      CppExpr array = CppAccess.mk(CppMethodCall.mkThruPtr(CppVar.mk(\"t\"), \"as_complex\"), \"val\");\n      CppExpr base = CppSubscript.mk(array, CppConst.mkInt(0));\n      String type = big ? \"int64_t\" : \"int32_t\";\n      String func = \"serialize_bit_string<\" + type + \", \" + n + \">\";\n      CppExpr arg = CppAccess.mk(CppMethodCall.mkThruPtr(base, \"as_base<\" + type + \">\"), \"val\");\n      return CppFuncCall.mk(func, arg).toStmt();\n    }\n\n    private CppStmt genSerializeBvToBv(int from, int to, boolean signed) {\n      String func = \"serialize_bv_to_bv<\" + from + \", \" + to + \", \" + signed + \">\";\n      return mkCall(func);\n    }\n\n    private CppStmt genSerializeFpToBv(int width, boolean signed) {\n      String func = \"serialize_fp_to_bv<\" + width + \", \" + signed + \">\";\n      return mkCall(func);\n    }\n\n    private CppStmt genSerializeFp(int e, int s, boolean big) {\n      String type = big ? \"double\" : \"float\";\n      String func = \"serialize_fp<\" + type + \", \" + e + \", \" + s + \">\";\n      CppExpr arg = CppFuncCall.mk(\"arg0\", CppVar.mk(\"t\"));\n      return CppFuncCall.mk(func, arg).toStmt();\n    }\n\n    private CppStmt genSerializeInt(boolean big) {\n      String type = big ? \"int64_t\" : \"int32_t\";\n      String func = \"serialize_int<\" + type + \">\";\n      return mkCall(func);\n    }\n\n    private CppStmt genSerializeQuantifier(boolean exists) {\n      String func = \"serialize_quantifier<\" + exists + \">\";\n      return mkCall(func);\n    }\n\n    public void genNeedsTypeAnnotationCases() {\n      boolean foundOne = false;\n      for (ConstructorSymbol sym : ctx.getConstructorSymbols()) {\n        if (SmtLibShim.needsTypeAnnotation(sym)) {\n          out.println(\"    case \" + ctx.lookupRepr(sym) + \":\");\n          foundOne = true;\n        }\n      }\n      if (foundOne) {\n        CppReturn.mk(CppConst.mkTrue()).println(out, 3);\n      }\n    }\n\n    public void genSymbolSerializationCases() {\n      boolean foundOne = false;\n      for (ConstructorSymbol sym : ctx.getConstructorSymbols()) {\n        if (sym.getConstructorSymbolType()\n            .equals(ConstructorSymbolType.SOLVER_CONSTRUCTOR_TESTER)) {\n          out.println(\"    case \" + ctx.lookupRepr(sym) + \":\");\n          foundOne = true;\n        }\n      }\n      if (foundOne) {\n        CppExpr call = CppFuncCall.mk(\"serialize_tester\", CppVar.mk(\"sym\"));\n        CppReturn.mk(call).println(out, 3);\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/SouffleCodeGen.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2022-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen;\n\nimport edu.harvard.seas.pl.formulog.Configuration;\nimport edu.harvard.seas.pl.formulog.codegen.ast.souffle.*;\nimport edu.harvard.seas.pl.formulog.symbols.RelationSymbol;\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport edu.harvard.seas.pl.formulog.validating.Stratum;\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\npublic class SouffleCodeGen {\n\n  private final CodeGenContext ctx;\n  private static final String non_existent_input = \"CODEGEN_IMPOSSIBLE_IN\";\n  private static final String non_existent_output = \"CODEGEN_IMPOSSIBLE_OUT\";\n  private static final SAtom non_existent_input_atom =\n      new SAtom(non_existent_input, Collections.emptyList(), false);\n\n  public SouffleCodeGen(CodeGenContext ctx) {\n    this.ctx = ctx;\n  }\n\n  public void gen(File directory) throws CodeGenException, IOException {\n    QueryPlanner qp = chooseQueryPlanner();\n    Pair<List<SRule>, List<Stratum>> p = new RuleTranslator(ctx, qp).translate(ctx.getProgram());\n    var rules = p.fst();\n    ctx.registerCustomRelation(non_existent_input, 0, SRuleMode.INPUT);\n    ctx.registerCustomRelation(non_existent_output, 0, SRuleMode.OUTPUT);\n    rules.add(genRuleForRetainingInputRelations());\n    rules.addAll(genRulesForEnforcingStratification(p.snd()));\n    File dlFile = directory.toPath().resolve(Path.of(\"src\", \"formulog.dl\")).toFile();\n    emitDlFile(dlFile, rules);\n    new FunctorCodeGen(ctx).emitFunctors(directory);\n  }\n\n  private QueryPlanner chooseQueryPlanner() throws CodeGenException {\n    switch (Configuration.optimizationSetting) {\n      case 0:\n        return new NopQueryPlanner();\n      case 5:\n        return new DeltaFirstQueryPlanner(ctx);\n      default:\n        throw new CodeGenException(\n            \"Unsupported optimization setting for codegen: \" + Configuration.optimizationSetting);\n    }\n  }\n\n  private void emitDlFile(File dlFile, List<SRule> rules) throws CodeGenException {\n    PrintWriter writer;\n    try {\n      writer = new PrintWriter(dlFile);\n    } catch (FileNotFoundException e) {\n      throw new CodeGenException(e);\n    }\n    Worker worker = new Worker(writer);\n    worker.declareTypes();\n    writer.println();\n    worker.declareRelations();\n    writer.println();\n    worker.declareFunctors();\n    writer.println();\n    for (SRule rule : rules) {\n      writer.println(rule);\n    }\n    writer.close();\n  }\n\n  SRule genRuleForRetainingInputRelations() {\n    SAtom head = new SAtom(non_existent_output, Collections.emptyList(), false);\n    List<SLit> body = new ArrayList<>();\n    body.add(non_existent_input_atom);\n    for (RelationSymbol sym : ctx.getProgram().getFactSymbols()) {\n      body.add(makeAtomWithDummyArgs(sym, false));\n    }\n    return new SRule(head, body);\n  }\n\n  private List<SRule> genRulesForEnforcingStratification(List<Stratum> strats) {\n    if (strats.isEmpty()) {\n      return Collections.emptyList();\n    }\n    List<SRule> l = new ArrayList<>();\n    RelationSymbol firstRepr = strats.get(0).getPredicateSyms().iterator().next();\n    var prevAtom = makeAtomWithDummyArgs(firstRepr, true);\n    for (int i = 1; i < strats.size(); ++i) {\n      RelationSymbol currRepr = strats.get(i).getPredicateSyms().iterator().next();\n      var currAtom = makeAtomWithDummyArgs(currRepr, false);\n      l.add(new SRule(currAtom, non_existent_input_atom, prevAtom));\n      prevAtom = currAtom;\n    }\n    return l;\n  }\n\n  private SAtom makeAtomWithDummyArgs(RelationSymbol sym, boolean negated) {\n    List<STerm> args = new ArrayList<>();\n    for (int i = 0; i < sym.getArity(); ++i) {\n      args.add(new SInt(0));\n    }\n    return new SAtom(ctx.lookupRepr(sym), args, negated);\n  }\n\n  private class Worker {\n\n    private final PrintWriter writer;\n\n    public Worker(PrintWriter writer) {\n      this.writer = writer;\n    }\n\n    public void declareTypes() {\n      for (SIntListType ty : ctx.getSouffleTypes()) {\n        writer.print(\".type \");\n        writer.print(ty.getName());\n        writer.print(\" = \");\n        writer.println(ty.getDef());\n      }\n    }\n\n    public void declareRelations() {\n      for (RelationSymbol sym : ctx.getProgram().getFactSymbols()) {\n        declareRelation(sym, SRuleMode.INPUT);\n      }\n      for (RelationSymbol sym : ctx.getProgram().getRuleSymbols()) {\n        declareRelation(sym, SRuleMode.OUTPUT);\n      }\n      for (Pair<String, Pair<Integer, SRuleMode>> e : ctx.getCustomRelations()) {\n        declareRelation(e.fst(), e.snd().fst(), e.snd().snd());\n      }\n    }\n\n    private void declareRelation(String name, int arity, SRuleMode mode) {\n      writer.print(\".decl \");\n      writer.print(name);\n      writer.print(\"(\");\n      for (int i = 0; i < arity; ++i) {\n        writer.print(\"x\");\n        writer.print(i);\n        writer.print(\":number\");\n        if (i < arity - 1) {\n          writer.print(\", \");\n        }\n      }\n      writer.println(\")\");\n      switch (mode) {\n        case INPUT:\n          writer.print(\".input \");\n          break;\n        case OUTPUT:\n          writer.print(\".output \");\n          break;\n        case INTERMEDIATE:\n          return;\n      }\n      writer.println(name);\n    }\n\n    private void declareRelation(RelationSymbol sym, SRuleMode mode) {\n      declareRelation(ctx.lookupRepr(sym), sym.getArity(), mode);\n    }\n\n    public void declareFunctors() {\n      writer.println(\".functor nth(n:number, ref:number, check:number):number\");\n      for (Pair<String, SFunctorBody> p : ctx.getFunctors()) {\n        String name = p.fst();\n        SFunctorBody body = p.snd();\n        declareFunctor(name, body);\n      }\n    }\n\n    private void declareFunctor(String name, SFunctorBody body) {\n      writer.print(\".functor \");\n      writer.print(name);\n      writer.print(\"(\");\n      for (int i = 0; i < body.getArity(); ++i) {\n        writer.print(\"x\");\n        writer.print(i);\n        writer.print(\":number\");\n        if (i < body.getArity() - 1) {\n          writer.print(\", \");\n        }\n      }\n      writer.print(\"):\");\n      writer.print(body.getRetType());\n      if (body.isStateful()) {\n        writer.print(\" stateful\");\n      }\n      writer.println();\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/SymbolCpp.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2020-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen;\n\nimport edu.harvard.seas.pl.formulog.codegen.ast.cpp.*;\nimport edu.harvard.seas.pl.formulog.symbols.ConstructorSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.GlobalSymbolManager.TupleSymbol;\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.util.Set;\n\npublic class SymbolCpp extends TemplateSrcFile {\n\n  public SymbolCpp(CodeGenContext ctx) {\n    super(\"Symbol.cpp\", ctx);\n  }\n\n  public void gen(BufferedReader br, PrintWriter out) throws IOException {\n    Worker w = new Worker(out);\n    CodeGenUtil.copyOver(br, out, 0);\n    w.defineSerialization();\n    CodeGenUtil.copyOver(br, out, 1);\n    w.initializeSymbolTable();\n    CodeGenUtil.copyOver(br, out, 2);\n    w.defineTupleLookup();\n    CodeGenUtil.copyOver(br, out, -1);\n  }\n\n  private class Worker {\n\n    private final Set<ConstructorSymbol> symbols = ctx.getConstructorSymbols();\n    private final PrintWriter out;\n\n    public Worker(PrintWriter out) {\n      this.out = out;\n    }\n\n    void defineSerialization() {\n      for (ConstructorSymbol sym : symbols) {\n        out.print(\"    case \");\n        out.print(ctx.lookupRepr(sym));\n        out.print(\": return out << \\\"\");\n        out.print(sym);\n        out.println(\"\\\";\");\n      }\n    }\n\n    void initializeSymbolTable() {\n      for (ConstructorSymbol sym : symbols) {\n        CppExpr access =\n            CppSubscript.mk(CppVar.mk(\"symbol_table\"), CppConst.mkString(CodeGenUtil.mkName(sym)));\n        CppExpr assign = CppBinop.mkAssign(access, CppVar.mk(ctx.lookupRepr(sym)));\n        assign.toStmt().println(out, 1);\n      }\n    }\n\n    void defineTupleLookup() {\n      for (ConstructorSymbol sym : symbols) {\n        if (sym instanceof TupleSymbol) {\n          out.print(\"    case \" + sym.getArity() + \": return \");\n          out.print(ctx.lookupRepr(sym));\n          out.println(\";\");\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/SymbolHpp.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2020-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen;\n\nimport edu.harvard.seas.pl.formulog.symbols.ConstructorSymbol;\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.util.Set;\n\npublic class SymbolHpp extends TemplateSrcFile {\n\n  public SymbolHpp(CodeGenContext ctx) {\n    super(\"Symbol.hpp\", ctx);\n  }\n\n  public void gen(BufferedReader br, PrintWriter out) throws IOException {\n    Worker w = new Worker(out);\n    CodeGenUtil.copyOver(br, out, 0);\n    w.declareSymbols();\n    CodeGenUtil.copyOver(br, out, 1);\n    w.defineArity();\n    CodeGenUtil.copyOver(br, out, -1);\n  }\n\n  private class Worker {\n\n    private final Set<ConstructorSymbol> symbols = ctx.getConstructorSymbols();\n    private final PrintWriter out;\n\n    public Worker(PrintWriter out) {\n      this.out = out;\n    }\n\n    void declareSymbols() {\n      for (ConstructorSymbol sym : symbols) {\n        out.print(\"  \");\n        out.println(ctx.lookupUnqualifiedRepr(sym) + \",\");\n      }\n    }\n\n    void defineArity() {\n      for (ConstructorSymbol sym : symbols) {\n        out.print(\"    case \");\n        out.print(ctx.lookupRepr(sym));\n        out.println(\": return \" + sym.getArity() + \";\");\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/TemplateSrcFile.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2022-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen;\n\nimport java.io.*;\n\npublic abstract class TemplateSrcFile {\n\n  private final String name;\n  protected final CodeGenContext ctx;\n\n  public TemplateSrcFile(String name, CodeGenContext ctx) {\n    this.name = name;\n    this.ctx = ctx;\n  }\n\n  void gen(File outDir) throws IOException, CodeGenException {\n    try (InputStream is = CodeGenUtil.inputSrcFile(name);\n        InputStreamReader isr = new InputStreamReader(is);\n        BufferedReader br = new BufferedReader(isr);\n        PrintWriter out = new PrintWriter(CodeGenUtil.outputSrcFile(outDir, name))) {\n      gen(br, out);\n      out.flush();\n    }\n  }\n\n  abstract void gen(BufferedReader br, PrintWriter out) throws IOException, CodeGenException;\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/TermCodeGen.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2020-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen;\n\nimport edu.harvard.seas.pl.formulog.ast.Constructor;\nimport edu.harvard.seas.pl.formulog.ast.Expr;\nimport edu.harvard.seas.pl.formulog.ast.Exprs.ExprVisitor;\nimport edu.harvard.seas.pl.formulog.ast.Fold;\nimport edu.harvard.seas.pl.formulog.ast.FunctionCallFactory.FunctionCall;\nimport edu.harvard.seas.pl.formulog.ast.LetFunExpr;\nimport edu.harvard.seas.pl.formulog.ast.MatchExpr;\nimport edu.harvard.seas.pl.formulog.ast.Primitive;\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.ast.Terms.TermVisitor;\nimport edu.harvard.seas.pl.formulog.ast.Var;\nimport edu.harvard.seas.pl.formulog.codegen.ast.cpp.*;\nimport edu.harvard.seas.pl.formulog.symbols.ConstructorSymbol;\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\npublic class TermCodeGen {\n\n  private final CodeGenContext ctx;\n\n  public TermCodeGen(CodeGenContext ctx) {\n    this.ctx = ctx;\n  }\n\n  public CppExpr mkBool(CppExpr bool) {\n    return CppFuncCall.mk(\"Term::make<bool>\", bool);\n  }\n\n  /*\n   * EZ: mkComplex no longer requires supporting statements, but these methods\n   * still exist in order to not break the rest of the code.\n   *\n   * AB: Probably makes sense to keep the current signature - I don't think it\n   * causes any significant harm, and it gives us some future flexibility.\n   */\n\n  /**\n   * Generate the C++ representation of a complex term given the symbol of a constructor and its\n   * arguments.\n   *\n   * @param sym\n   * @param args\n   * @return A pair of a C++ statement and a C++ expression representing the complex term; the\n   *     statement should be executed before the expression is used.\n   */\n  public Pair<CppStmt, CppExpr> mkComplex(ConstructorSymbol sym, List<CppExpr> args) {\n    assert sym.getArity() == args.size();\n    CppVar symbol = CppVar.mk(ctx.lookupRepr(sym));\n    CppExpr val = CppFuncCall.mk(\"Term::make<\" + symbol + \">\", args);\n    return new Pair<>(CppSeq.mk(), val);\n  }\n\n  /**\n   * Generate the C++ representation of a complex term given the symbol of a constructor and its\n   * arguments.\n   *\n   * @param sym\n   * @param args\n   * @return A pair of a C++ statement and a C++ expression representing the complex term; the\n   *     statement should be executed before the expression is used.\n   */\n  public Pair<CppStmt, CppExpr> mkComplex(ConstructorSymbol sym, CppExpr... args) {\n    return mkComplex(sym, Arrays.asList(args));\n  }\n\n  /**\n   * Generate the C++ representation of a complex term given the symbol of a constructor and its\n   * arguments.\n   *\n   * @param acc An accumulator list of statements; these should be executed before the returned\n   *     expression is used\n   * @param sym\n   * @param args\n   * @return A C++ expression representing the complex term\n   */\n  public CppExpr mkComplex(List<CppStmt> acc, ConstructorSymbol sym, CppExpr... args) {\n    Pair<CppStmt, CppExpr> p = mkComplex(sym, args);\n    acc.add(p.fst());\n    return p.snd();\n  }\n\n  /**\n   * Generate the C++ representation of a complex term given the symbol of a constructor and its\n   * arguments.\n   *\n   * @param acc An accumulator list of statements; these should be executed before the returned\n   *     expression is used\n   * @param sym\n   * @param args\n   * @return A C++ expression representing the complex term\n   */\n  public CppExpr mkComplex(List<CppStmt> acc, ConstructorSymbol sym, List<CppExpr> args) {\n    Pair<CppStmt, CppExpr> p = mkComplex(sym, args);\n    acc.add(p.fst());\n    return p.snd();\n  }\n\n  /**\n   * Generate the C++ representation of a term, given an environment mapping Formulog variables to\n   * C++ expressions.\n   *\n   * @param t\n   * @param env\n   * @return A pair of a C++ statement and a C++ expression representing the term; the statement\n   *     should be executed before the expression is used.\n   */\n  public Pair<CppStmt, CppExpr> gen(Term t, Map<Var, CppExpr> env) {\n    List<CppStmt> acc = new ArrayList<>();\n    CppExpr e = gen(acc, t, env);\n    return new Pair<>(CppSeq.mk(acc), e);\n  }\n\n  /**\n   * Generate the C++ representation of a term, given an environement mapping Formulog variables to\n   * C++ expressions.\n   *\n   * @param acc An accumulator list of statements; these should be executed before the returned\n   *     expression is used\n   * @param sym\n   * @param args\n   * @return A C++ expression representing the term\n   */\n  public CppExpr gen(List<CppStmt> acc, Term t, Map<Var, CppExpr> env) {\n    return new Worker(acc, env).go(t);\n  }\n\n  /**\n   * Generate the C++ representation of a term, given an environement mapping Formulog variables to\n   * C++ expressions.\n   *\n   * @param acc An accumulator list of statements; these should be executed before the returned\n   *     expression is used\n   * @param sym\n   * @param args\n   * @return C++ expressions representing the terms (in order)\n   */\n  public List<CppExpr> gen(List<CppStmt> acc, List<Term> ts, Map<Var, CppExpr> env) {\n    List<CppExpr> exprs = new ArrayList<>();\n    for (Term t : ts) {\n      exprs.add(gen(acc, t, env));\n    }\n    return exprs;\n  }\n\n  /**\n   * Generate the C++ representation of some terms, given an environment mapping Formulog variables\n   * to C++ expressions.\n   *\n   * @param ts\n   * @param env\n   * @return A pair of a C++ statement and a list of C++ expressions representing the terms (in\n   *     order); the statement should be executed before the expressions are used.\n   */\n  public Pair<CppStmt, List<CppExpr>> gen(List<Term> ts, Map<Var, CppExpr> env) {\n    List<CppStmt> acc = new ArrayList<>();\n    List<CppExpr> es = gen(acc, ts, env);\n    return new Pair<>(CppSeq.mk(acc), es);\n  }\n\n  private class Worker {\n\n    private final Map<Var, CppExpr> env;\n    private final List<CppStmt> acc;\n    private final MatchCodeGen mcg = new MatchCodeGen(ctx);\n\n    public Worker(List<CppStmt> acc, Map<Var, CppExpr> env) {\n      this.acc = acc;\n      this.env = env;\n    }\n\n    public CppExpr go(Term t) {\n      return t.accept(tv, null);\n    }\n\n    private final TermVisitor<Void, CppExpr> tv =\n        new TermVisitor<Void, CppExpr>() {\n\n          @Override\n          public CppExpr visit(Var x, Void in) {\n            assert env.containsKey(x);\n            return env.get(x);\n          }\n\n          @Override\n          public CppExpr visit(Constructor c, Void in) {\n            ConstructorSymbol sym = c.getSymbol();\n            Term[] args = c.getArgs();\n            List<CppExpr> cppArgs = new ArrayList<>();\n            for (Term arg : args) {\n              cppArgs.add(arg.accept(this, in));\n            }\n            CppExpr term = mkComplex(acc, sym, cppArgs);\n            String tId = ctx.newId(\"t\");\n            acc.add(CppDecl.mk(tId, term));\n            return CppVar.mk(tId);\n          }\n\n          @Override\n          public CppExpr visit(Primitive<?> p, Void in) {\n            return CppBaseTerm.mk(p);\n          }\n\n          @Override\n          public CppExpr visit(Expr e, Void in) {\n            return e.accept(ev, in);\n          }\n        };\n\n    private final ExprVisitor<Void, CppExpr> ev =\n        new ExprVisitor<Void, CppExpr>() {\n\n          @Override\n          public CppExpr visit(MatchExpr matchExpr, Void in) {\n            Pair<CppStmt, CppExpr> p = mcg.gen(matchExpr, env);\n            acc.add(p.fst());\n            return p.snd();\n          }\n\n          @Override\n          public CppExpr visit(FunctionCall funcCall, Void in) {\n            List<CppExpr> args = new ArrayList<>();\n            for (Term arg : funcCall.getArgs()) {\n              args.add(arg.accept(tv, in));\n            }\n            return CppFuncCall.mk(ctx.lookupRepr(funcCall.getSymbol()), args);\n          }\n\n          @Override\n          public CppExpr visit(LetFunExpr funcDefs, Void in) {\n            throw new AssertionError(\"impossible\");\n          }\n\n          @Override\n          public CppExpr visit(Fold fold, Void in) {\n            List<CppExpr> args = new ArrayList<>();\n            var f = ctx.lookupRepr(fold.getFunction());\n            args.add(CppExprFromString.mk(f));\n            for (Term arg : fold.getArgs()) {\n              args.add(arg.accept(tv, in));\n            }\n            return CppFuncCall.mk(\"funcs::fold\", args);\n          }\n        };\n  }\n\n  public static CppExpr genIntizeTerm(CppExpr term) {\n    return CppMethodCall.mkThruPtr(term, \"intize\");\n  }\n\n  public static CppExpr genUnintizeTerm(CppExpr id) {\n    return CppFuncCall.mk(\"Term::unintize\", id);\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/TermCpp.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2020-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen;\n\nimport edu.harvard.seas.pl.formulog.symbols.ConstructorSymbol;\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.util.Collections;\nimport java.util.Set;\n\npublic class TermCpp extends TemplateSrcFile {\n\n  public TermCpp(CodeGenContext ctx) {\n    super(\"Term.cpp\", ctx);\n  }\n\n  public void gen(BufferedReader br, PrintWriter out) throws IOException {\n    Worker w = new Worker(out);\n    CodeGenUtil.copyOver(br, out, 0);\n    w.declareExplicitTemplateInstantiations();\n    CodeGenUtil.copyOver(br, out, 1);\n    w.declareMakeGenericCases();\n    CodeGenUtil.copyOver(br, out, -1);\n  }\n\n  private class Worker {\n\n    private final Set<ConstructorSymbol> symbols = ctx.getConstructorSymbols();\n    private final PrintWriter out;\n\n    public Worker(PrintWriter out) {\n      this.out = out;\n    }\n\n    void declareExplicitTemplateInstantiations() {\n      for (ConstructorSymbol sym : symbols) {\n        String args = String.join(\", \", Collections.nCopies(sym.getArity(), \"term_ptr\"));\n        out.print(\"template term_ptr Term::make<\");\n        out.print(ctx.lookupRepr(sym));\n        if (sym.getArity() != 0) {\n          out.print(\", \" + args);\n        }\n        out.println(\">(\" + args + \");\");\n      }\n    }\n\n    void declareMakeGenericCases() {\n      for (ConstructorSymbol sym : symbols) {\n        String symName = ctx.lookupRepr(sym);\n        int arity = sym.getArity();\n        String[] args = new String[arity];\n        for (int i = 0; i < arity; i++) args[i] = \"terms[\" + i + \"]\";\n        out.printf(\"    case %s:\\n\", symName);\n        out.printf(\"      return Term::make<%s>(%s);\\n\", symName, String.join(\", \", args));\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/TypeCodeGen.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2020-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen;\n\nimport edu.harvard.seas.pl.formulog.codegen.ast.cpp.*;\nimport edu.harvard.seas.pl.formulog.symbols.BuiltInTypeSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.TypeSymbol;\nimport edu.harvard.seas.pl.formulog.types.FunctorType;\nimport edu.harvard.seas.pl.formulog.types.Types.AlgebraicDataType;\nimport edu.harvard.seas.pl.formulog.types.Types.OpaqueType;\nimport edu.harvard.seas.pl.formulog.types.Types.Type;\nimport edu.harvard.seas.pl.formulog.types.Types.TypeIndex;\nimport edu.harvard.seas.pl.formulog.types.Types.TypeVar;\nimport edu.harvard.seas.pl.formulog.types.Types.TypeVisitor;\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\npublic class TypeCodeGen {\n\n  private final CodeGenContext ctx;\n\n  public TypeCodeGen(CodeGenContext ctx) {\n    this.ctx = ctx;\n  }\n\n  /**\n   * Take a Formulog type and generate its C++ representation.\n   *\n   * @param type The Formulog type that needs to be represented in C++\n   * @return A pair of a C++ statement and an C++ expression. The expression is the C++\n   *     representation of the provided type; the statement is code that should be evaluated before\n   *     that expression is used.\n   */\n  public Pair<CppStmt, CppExpr> gen(Type type) {\n    List<CppStmt> acc = new ArrayList<>();\n    CppExpr e = gen(acc, type);\n    return new Pair<>(CppSeq.mk(acc), e);\n  }\n\n  /**\n   * Take a Formulog type and generate its C++ representation. Any C++ statements that need to be\n   * run before the expression (representing that type) is used are appended to the given\n   * accumulator list.\n   *\n   * @param acc An accumulator list of statements\n   * @param type The Formulog type that needs to be represented in C++\n   * @return The C++ expression representing the given Formulog type\n   */\n  public CppExpr gen(List<CppStmt> acc, Type type) {\n    return new Worker(acc, new HashMap<>()).go(type);\n  }\n\n  /**\n   * Take a list of Formulog types and generate their C++ representation. Any C++ statements that\n   * need to be run before the expressions (representing those types) are used are appended to the\n   * given accumulator list.\n   *\n   * @param acc An accumulator list of statements\n   * @param types The Formulog types that need to be represented in C++\n   * @return The C++ expressions representing the given Formulog types (and in the same order)\n   */\n  public List<CppExpr> gen(List<CppStmt> acc, List<Type> types) {\n    List<CppExpr> es = new ArrayList<>();\n    for (Type ty : types) {\n      gen(acc, ty);\n    }\n    return es;\n  }\n\n  /**\n   * Take a list of Formulog types and generate their C++ representations.\n   *\n   * @param types The Formulog types that need to be represented in C++\n   * @return A pair of a C++ statement and a list of C++ expression. The expressions are the C++\n   *     representations of the provided types (in the same order); the statement is code that\n   *     should be evaluated before those expressions are used.\n   */\n  public Pair<CppStmt, List<CppExpr>> gen(List<Type> types) {\n    List<CppStmt> acc = new ArrayList<>();\n    List<CppExpr> es = gen(acc, types);\n    return new Pair<>(CppSeq.mk(acc), es);\n  }\n\n  private class Worker {\n\n    // Keep track of which type variables have already been generated\n    private final Map<TypeVar, CppExpr> env;\n    private final List<CppStmt> acc;\n\n    public Worker(List<CppStmt> acc, Map<TypeVar, CppExpr> env) {\n      this.acc = acc;\n      this.env = env;\n    }\n\n    public CppExpr go(Type type) {\n      if (type instanceof FunctorType) {\n        return go1((FunctorType) type);\n      }\n      return type.accept(visitor, null);\n    }\n\n    private List<CppExpr> go(List<Type> types) {\n      List<CppExpr> l = new ArrayList<>();\n      for (Type ty : types) {\n        l.add(go(ty));\n      }\n      return l;\n    }\n\n    private CppExpr go1(FunctorType type) {\n      CppExpr ret = go(type.getRetType());\n      // A FunctorType is represented in C++ by a pair of the argument types and the\n      // return type.\n      return CppFuncCall.mk(\"make_pair\", mkVec(type.getArgTypes()), ret);\n    }\n\n    TypeVisitor<Void, CppExpr> visitor =\n        new TypeVisitor<Void, CppExpr>() {\n\n          @Override\n          public CppExpr visit(TypeVar typeVar, Void in) {\n            CppExpr e = env.get(typeVar);\n            if (e == null) {\n              // Tell the C++ code to generate a fresh type variable\n              String id = ctx.newId(\"ty\");\n              acc.add(CppDecl.mk(id, CppFuncCall.mk(\"new_var\")));\n              e = CppVar.mk(id);\n              env.put(typeVar, e);\n            }\n            return e;\n          }\n\n          @Override\n          public CppExpr visit(AlgebraicDataType algebraicType, Void in) {\n            TypeSymbol sym = algebraicType.getSymbol();\n            CppExpr e = null;\n            if (sym instanceof BuiltInTypeSymbol) {\n              e = handleBuiltInType(algebraicType);\n            }\n            if (e != null) {\n              return e;\n            }\n            // XXX Need to make sure this matches SMT declarations\n            return mkType(\"|\" + sym + \"|\", algebraicType.getTypeArgs());\n          }\n\n          @Override\n          public CppExpr visit(OpaqueType opaqueType, Void in) {\n            throw new AssertionError(\"impossible\");\n          }\n\n          @Override\n          public CppExpr visit(TypeIndex typeIndex, Void in) {\n            return mkType(Integer.toString(typeIndex.getIndex()));\n          }\n        };\n\n    private CppExpr handleBuiltInType(AlgebraicDataType type) {\n      BuiltInTypeSymbol sym = (BuiltInTypeSymbol) type.getSymbol();\n      List<Type> args = type.getTypeArgs();\n      switch (sym) {\n        case ARRAY_TYPE:\n          return mkType(\"Array\", args);\n        case BOOL_TYPE:\n          return mkType(\"Bool\", args);\n        case BV:\n          return mkType(\"_ BitVec\", args);\n        case STRING_TYPE:\n          return mkType(\"String\", args);\n        case FP:\n          return mkType(\"_ FloatingPoint\", args);\n        case INT_TYPE:\n          return mkType(\"Int\", args);\n        // The rest of the built-in types can be treated as normal (i.e., user-defined)\n        // types\n        case LIST_TYPE:\n        case OPTION_TYPE:\n        case CMP_TYPE:\n        case MODEL_TYPE:\n        case SMT_PATTERN_TYPE:\n        case SMT_TYPE:\n        case SMT_WRAPPED_VAR_TYPE:\n        case SYM_TYPE:\n        case OPAQUE_SET:\n          break;\n      }\n      return null;\n    }\n\n    private CppExpr mkType(String name) {\n      return mkType(name, Collections.emptyList());\n    }\n\n    private CppExpr mkType(String name, List<Type> args) {\n      CppExpr cppName = CppConst.mkString(name);\n      CppExpr vec = mkVec(args);\n      String tyId = ctx.newId(\"ty\");\n      // Create a new variable and initialize it. The initializer has the signature\n      // (type_name, is_var, type_args).\n      acc.add(CppCtor.mkInitializer(\"Type\", tyId, cppName, CppConst.mkFalse(), vec));\n      return CppVar.mk(tyId);\n    }\n\n    private CppExpr mkVec(List<Type> args) {\n      String vId = ctx.newId(\"v\");\n      acc.add(CppCtor.mkInitializer(\"vector<Type>\", vId, go(args)));\n      return CppVar.mk(vId);\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/TypeCpp.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen;\n\nimport edu.harvard.seas.pl.formulog.codegen.ast.cpp.CppExpr;\nimport edu.harvard.seas.pl.formulog.codegen.ast.cpp.CppReturn;\nimport edu.harvard.seas.pl.formulog.codegen.ast.cpp.CppSeq;\nimport edu.harvard.seas.pl.formulog.codegen.ast.cpp.CppStmt;\nimport edu.harvard.seas.pl.formulog.symbols.ConstructorSymbol;\nimport edu.harvard.seas.pl.formulog.types.FunctorType;\nimport edu.harvard.seas.pl.formulog.types.TypeChecker;\nimport edu.harvard.seas.pl.formulog.types.Types.Type;\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class TypeCpp extends TemplateSrcFile {\n\n  public TypeCpp(CodeGenContext ctx) {\n    super(\"Type.cpp\", ctx);\n  }\n\n  public void gen(BufferedReader br, PrintWriter out) throws IOException {\n    Worker pr = new Worker(out);\n    CodeGenUtil.copyOver(br, out, 0);\n    pr.defineSymbolTypes();\n    CodeGenUtil.copyOver(br, out, -1);\n  }\n\n  private class Worker {\n\n    private final PrintWriter out;\n    private final TypeCodeGen tcg = new TypeCodeGen(ctx);\n\n    public Worker(PrintWriter out) {\n      this.out = out;\n    }\n\n    public void defineSymbolTypes() {\n      for (ConstructorSymbol sym : ctx.getConstructorSymbols()) {\n        defineSymbolType(sym);\n      }\n    }\n\n    private void defineSymbolType(ConstructorSymbol sym) {\n      out.println(\"    case \" + ctx.lookupRepr(sym) + \": {\");\n      genCaseBody(sym).println(out, 3);\n      out.println(\"    }\");\n    }\n\n    private CppStmt genCaseBody(ConstructorSymbol sym) {\n      List<CppStmt> acc = new ArrayList<>();\n      FunctorType ft = simplify(sym.getCompileTimeType());\n      CppExpr typeCode = tcg.gen(acc, ft);\n      acc.add(CppReturn.mk(typeCode));\n      return CppSeq.mk(acc);\n    }\n\n    private FunctorType simplify(FunctorType ft) {\n      List<Type> args = new ArrayList<>();\n      for (Type ty : ft.getArgTypes()) {\n        args.add(TypeChecker.simplify(ty));\n      }\n      return new FunctorType(args, TypeChecker.simplify(ft.getRetType()));\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/cpp/CppAccess.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.cpp;\n\nimport java.io.PrintWriter;\n\npublic class CppAccess implements CppExpr {\n\n  private final CppExpr val;\n  private final String field;\n  private final boolean thruPtr;\n\n  private CppAccess(CppExpr val, String field, boolean thruPtr) {\n    this.val = val;\n    this.field = field;\n    this.thruPtr = thruPtr;\n  }\n\n  public static CppAccess mk(CppExpr val, String field) {\n    return new CppAccess(val, field, false);\n  }\n\n  public static CppAccess mkThruPtr(CppExpr val, String field) {\n    return new CppAccess(val, field, true);\n  }\n\n  @Override\n  public void print(PrintWriter out) {\n    val.print(out);\n    out.print(thruPtr ? \"->\" : \".\");\n    out.print(field);\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/cpp/CppBaseTerm.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2022-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.cpp;\n\nimport edu.harvard.seas.pl.formulog.ast.BoolTerm;\nimport edu.harvard.seas.pl.formulog.ast.FP32;\nimport edu.harvard.seas.pl.formulog.ast.FP64;\nimport edu.harvard.seas.pl.formulog.ast.I32;\nimport edu.harvard.seas.pl.formulog.ast.I64;\nimport edu.harvard.seas.pl.formulog.ast.Primitive;\nimport edu.harvard.seas.pl.formulog.ast.StringTerm;\nimport java.io.PrintWriter;\n\npublic class CppBaseTerm implements CppExpr {\n\n  private final Primitive<?> p;\n\n  public static CppBaseTerm mk(Primitive<?> p) {\n    return new CppBaseTerm(p);\n  }\n\n  private CppBaseTerm(Primitive<?> p) {\n    this.p = p;\n  }\n\n  @Override\n  public void print(PrintWriter out) {\n    String type;\n    String val = p.toString();\n    if (p instanceof I32) {\n      type = \"int32_t\";\n    } else if (p instanceof I64) {\n      type = \"int64_t\";\n    } else if (p instanceof FP32) {\n      type = \"float\";\n      val = handleFloat(((FP32) p).getVal());\n    } else if (p instanceof FP64) {\n      type = \"double\";\n      val = handleDouble(((FP64) p).getVal());\n    } else if (p instanceof BoolTerm) {\n      type = \"bool\";\n    } else if (p instanceof StringTerm) {\n      type = \"string\";\n    } else {\n      throw new UnsupportedOperationException(\"Unsupported primitive: \" + p);\n    }\n    printMakeTerm(type, val, out);\n  }\n\n  void printMakeTerm(String type, String val, PrintWriter out) {\n    out.print(\"Term::make<\");\n    out.print(type);\n    out.print(\">(\");\n    out.print(val);\n    out.print(\")\");\n  }\n\n  public static String handleDouble(double d) {\n    if (d == Double.NEGATIVE_INFINITY) {\n      return \"-numeric_limits<double>::infinity()\";\n    } else if (d == Double.POSITIVE_INFINITY) {\n      return \"numeric_limits<double>::infinity()\";\n    } else if (Double.isNaN(d)) {\n      return \"nan(\\\"\\\")\";\n    } else {\n      return Double.toString(d);\n    }\n  }\n\n  public static String handleFloat(float d) {\n    if (d == Float.NEGATIVE_INFINITY) {\n      return \"-numeric_limits<float>::infinity()\";\n    } else if (d == Float.POSITIVE_INFINITY) {\n      return \"numeric_limits<float>::infinity()\";\n    } else if (Float.isNaN(d)) {\n      return \"nanf(\\\"\\\")\";\n    } else {\n      return Float.toString(d);\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/cpp/CppBinop.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2022-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.cpp;\n\nimport java.io.PrintWriter;\n\npublic class CppBinop implements CppExpr {\n\n  private final CppExpr lhs;\n  private final String op;\n  private final CppExpr rhs;\n\n  private CppBinop(CppExpr lhs, String op, CppExpr rhs) {\n    this.lhs = lhs;\n    this.op = op;\n    this.rhs = rhs;\n  }\n\n  private static CppBinop mk(CppExpr lhs, String op, CppExpr rhs) {\n    return new CppBinop(lhs, op, rhs);\n  }\n\n  public static CppBinop mkOrUpdate(CppExpr lhs, CppExpr rhs) {\n    return mk(lhs, \"|=\", rhs);\n  }\n\n  public static CppBinop mkLogAnd(CppExpr lhs, CppExpr rhs) {\n    return mk(lhs, \"&&\", rhs);\n  }\n\n  public static CppBinop mkNotEq(CppExpr lhs, CppExpr rhs) {\n    return mk(lhs, \"!=\", rhs);\n  }\n\n  public static CppBinop mkLt(CppExpr lhs, CppExpr rhs) {\n    return mk(lhs, \"<\", rhs);\n  }\n\n  public static CppBinop mkAssign(CppExpr lhs, CppExpr rhs) {\n    return mk(lhs, \"=\", rhs);\n  }\n\n  public static CppExpr mkEq(CppExpr lhs, CppExpr rhs) {\n    return mk(lhs, \"==\", rhs);\n  }\n\n  public static CppExpr mkShiftLeft(CppExpr lhs, CppExpr rhs) {\n    return mk(lhs, \"<<\", rhs);\n  }\n\n  @Override\n  public void print(PrintWriter out) {\n    out.print(\"(\");\n    lhs.print(out);\n    out.print(\" \");\n    out.print(op);\n    out.print(\" \");\n    rhs.print(out);\n    out.print(\")\");\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/cpp/CppBlock.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.cpp;\n\nimport edu.harvard.seas.pl.formulog.codegen.CodeGenUtil;\nimport java.io.PrintWriter;\n\npublic class CppBlock implements CppStmt {\n\n  private final CppStmt stmt;\n\n  private CppBlock(CppStmt stmt) {\n    this.stmt = stmt;\n  }\n\n  public static CppBlock mk(CppStmt stmt) {\n    return new CppBlock(stmt);\n  }\n\n  @Override\n  public void println(PrintWriter out, int indent) {\n    CodeGenUtil.printIndent(out, indent);\n    out.println(\"{\");\n    stmt.println(out, indent + 1);\n    CodeGenUtil.printIndent(out, indent);\n    out.println(\"}\");\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/cpp/CppCast.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.cpp;\n\nimport java.io.PrintWriter;\n\npublic class CppCast implements CppExpr {\n\n  private final String type;\n  private final CppExpr expr;\n  private final String castName;\n\n  private CppCast(String castName, String type, CppExpr expr) {\n    this.type = type;\n    this.expr = expr;\n    this.castName = castName;\n  }\n\n  public static CppCast mkReinterpret(String type, CppExpr expr) {\n    return new CppCast(\"reinterpret_cast\", type, expr);\n  }\n\n  @Override\n  public void print(PrintWriter out) {\n    out.print(castName);\n    out.print(\"<\");\n    out.print(type);\n    out.print(\">\");\n    out.print(\"(\");\n    expr.print(out);\n    out.print(\")\");\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/cpp/CppConst.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.cpp;\n\nimport java.io.PrintWriter;\n\npublic class CppConst<T> implements CppExpr {\n\n  private final T val;\n\n  private CppConst(T val) {\n    this.val = val;\n  }\n\n  public static CppConst<Boolean> mkTrue() {\n    return new CppConst<>(true);\n  }\n\n  public static CppConst<Boolean> mkFalse() {\n    return new CppConst<>(false);\n  }\n\n  public static CppConst<Integer> mkInt(int i) {\n    return new CppConst<>(i);\n  }\n\n  public static CppConst<String> mkString(String s) {\n    return new CppConst<>(s);\n  }\n\n  @Override\n  public void print(PrintWriter out) {\n    boolean isString = val instanceof String;\n    if (isString) {\n      out.print(\"\\\"\");\n    }\n    out.print(val);\n    if (isString) {\n      out.print(\"\\\"\");\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/cpp/CppCtor.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2022-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.cpp;\n\nimport edu.harvard.seas.pl.formulog.codegen.CodeGenUtil;\nimport java.io.PrintWriter;\nimport java.util.Arrays;\nimport java.util.List;\n\npublic class CppCtor implements CppStmt {\n\n  private final String type;\n  private final String var;\n  private final List<CppExpr> args;\n  private final boolean initializer;\n\n  private CppCtor(String type, String var, List<CppExpr> args, boolean initializer) {\n    this.type = type;\n    this.var = var;\n    this.args = args;\n    this.initializer = initializer;\n  }\n\n  public static CppCtor mk(String type, String var, List<CppExpr> args) {\n    return new CppCtor(type, var, args, false);\n  }\n\n  public static CppCtor mk(String type, String var, CppExpr... args) {\n    return new CppCtor(type, var, Arrays.asList(args), false);\n  }\n\n  public static CppCtor mkInitializer(String type, String var, List<CppExpr> args) {\n    return new CppCtor(type, var, args, true);\n  }\n\n  public static CppCtor mkInitializer(String type, String var, CppExpr... args) {\n    return new CppCtor(type, var, Arrays.asList(args), true);\n  }\n\n  @Override\n  public void println(PrintWriter out, int indent) {\n    CodeGenUtil.printIndent(out, indent);\n    out.print(type);\n    out.print(\" \");\n    out.print(var);\n    if (!args.isEmpty()) {\n      out.print(initializer ? \"{\" : \"(\");\n      CodeGenUtil.printSeparated(args, \", \", out);\n      out.print(initializer ? \"}\" : \")\");\n    }\n    out.println(\";\");\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/cpp/CppDecl.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.cpp;\n\nimport edu.harvard.seas.pl.formulog.codegen.CodeGenUtil;\nimport java.io.PrintWriter;\n\npublic class CppDecl implements CppStmt {\n\n  private final String type;\n  private final String var;\n  private final CppExpr val;\n\n  private CppDecl(String type, String var, CppExpr val) {\n    this.type = type;\n    this.var = var;\n    this.val = val;\n  }\n\n  public static CppDecl mk(String var, CppExpr val) {\n    return new CppDecl(\"auto\", var, val);\n  }\n\n  public static CppDecl mkRef(String var, CppExpr val) {\n    return new CppDecl(\"auto&\", var, val);\n  }\n\n  @Override\n  public void println(PrintWriter out, int indent) {\n    CodeGenUtil.printIndent(out, indent);\n    out.print(type);\n    out.print(\" \");\n    out.print(var);\n    out.print(\" = \");\n    val.print(out);\n    out.println(\";\");\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/cpp/CppExpr.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.cpp;\n\nimport edu.harvard.seas.pl.formulog.codegen.CodeGenUtil;\nimport java.io.PrintWriter;\n\npublic interface CppExpr {\n\n  void print(PrintWriter out);\n\n  default CppStmt toStmt() {\n    return new CppStmt() {\n\n      @Override\n      public void println(PrintWriter out, int indent) {\n        CodeGenUtil.printIndent(out, indent);\n        CppExpr.this.print(out);\n        out.println(\";\");\n      }\n    };\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/cpp/CppExprFromString.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.cpp;\n\nimport java.io.PrintWriter;\n\npublic class CppExprFromString implements CppExpr {\n\n  private final String s;\n\n  private CppExprFromString(String s) {\n    this.s = s;\n  }\n\n  public static CppExprFromString mk(String s) {\n    return new CppExprFromString(s);\n  }\n\n  @Override\n  public void print(PrintWriter out) {\n    out.print(s);\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/cpp/CppFor.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.cpp;\n\nimport edu.harvard.seas.pl.formulog.codegen.CodeGenUtil;\nimport java.io.PrintWriter;\n\npublic class CppFor implements CppStmt {\n\n  private final String var;\n  private final CppExpr init;\n  private final CppExpr guard;\n  private final CppExpr update;\n  private final CppStmt body;\n  private final boolean parallel;\n\n  private CppFor(\n      String var, CppExpr init, CppExpr guard, CppExpr update, CppStmt body, boolean parallel) {\n    this.var = var;\n    this.init = init;\n    this.guard = guard;\n    this.update = update;\n    this.body = body;\n    this.parallel = parallel;\n  }\n\n  public static CppFor mk(String var, CppExpr init, CppExpr guard, CppExpr update, CppStmt body) {\n    return new CppFor(var, init, guard, update, body, false);\n  }\n\n  public static CppFor mkParallel(\n      String var, CppExpr init, CppExpr guard, CppExpr update, CppStmt body) {\n    return new CppFor(var, init, guard, update, body, true);\n  }\n\n  @Override\n  public void println(PrintWriter out, int indent) {\n    CodeGenUtil.printIndent(out, indent);\n    out.print(parallel ? \"pfor\" : \"for\");\n    out.print(\" (auto \");\n    out.print(var);\n    out.print(\" = \");\n    init.print(out);\n    out.print(\"; \");\n    guard.print(out);\n    out.print(\"; \");\n    update.print(out);\n    out.println(\") {\");\n    body.println(out, indent + 1);\n    CodeGenUtil.printIndent(out, indent);\n    out.println(\"}\");\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/cpp/CppForEach.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.cpp;\n\nimport edu.harvard.seas.pl.formulog.codegen.CodeGenUtil;\nimport java.io.PrintWriter;\n\npublic class CppForEach implements CppStmt {\n\n  private final String var;\n  private final CppExpr val;\n  private final CppStmt body;\n\n  private CppForEach(String var, CppExpr val, CppStmt body) {\n    this.var = var;\n    this.val = val;\n    this.body = body;\n  }\n\n  public static CppForEach mk(String var, CppExpr val, CppStmt body) {\n    return new CppForEach(var, val, body);\n  }\n\n  @Override\n  public void println(PrintWriter out, int indent) {\n    CodeGenUtil.printIndent(out, indent);\n    out.print(\"for (const auto& \");\n    out.print(var);\n    out.print(\" : \");\n    val.print(out);\n    out.println(\") {\");\n    body.println(out, indent + 1);\n    CodeGenUtil.printIndent(out, indent);\n    out.println(\"}\");\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/cpp/CppFuncCall.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.cpp;\n\nimport edu.harvard.seas.pl.formulog.codegen.CodeGenUtil;\nimport java.io.PrintWriter;\nimport java.util.Arrays;\nimport java.util.List;\n\npublic class CppFuncCall implements CppExpr {\n\n  private final String func;\n  private final List<CppExpr> args;\n\n  private CppFuncCall(String func, List<CppExpr> args) {\n    this.func = func;\n    this.args = args;\n  }\n\n  public static CppFuncCall mk(String func, List<CppExpr> args) {\n    return new CppFuncCall(func, args);\n  }\n\n  public static CppFuncCall mk(String func, CppExpr... args) {\n    return mk(func, Arrays.asList(args));\n  }\n\n  @Override\n  public void print(PrintWriter out) {\n    out.print(func);\n    out.print(\"(\");\n    CodeGenUtil.printSeparated(args, \", \", out);\n    out.print(\")\");\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/cpp/CppGoto.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.cpp;\n\nimport edu.harvard.seas.pl.formulog.codegen.CodeGenUtil;\nimport java.io.PrintWriter;\n\npublic class CppGoto implements CppStmt {\n\n  private final String label;\n\n  private CppGoto(String label) {\n    this.label = label;\n  }\n\n  public static CppGoto mk(String label) {\n    return new CppGoto(label);\n  }\n\n  @Override\n  public void println(PrintWriter out, int indent) {\n    CodeGenUtil.printIndent(out, indent);\n    out.println(\"goto \" + label + \";\");\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/cpp/CppIf.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2022-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.cpp;\n\nimport edu.harvard.seas.pl.formulog.codegen.CodeGenUtil;\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport java.io.PrintWriter;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\npublic class CppIf implements CppStmt {\n\n  private final List<Pair<CppExpr, CppStmt>> cases;\n  private final CppStmt elseBranch;\n\n  private CppIf(List<Pair<CppExpr, CppStmt>> cases, CppStmt elseBranch) {\n    if (cases.isEmpty()) {\n      throw new IllegalArgumentException(\"Need to have at least one case in an if statement\");\n    }\n    this.cases = cases;\n    this.elseBranch = elseBranch;\n  }\n\n  public static CppIf mk(CppExpr guard, CppStmt thenBranch) {\n    return mk(guard, thenBranch, null);\n  }\n\n  public static CppIf mk(CppExpr guard, CppStmt thenBranch, CppStmt elseBranch) {\n    return new CppIf(Collections.singletonList(new Pair<>(guard, thenBranch)), elseBranch);\n  }\n\n  public static CppIf mk(List<Pair<CppExpr, CppStmt>> cases) {\n    return new CppIf(new ArrayList<>(cases), null);\n  }\n\n  @Override\n  public void println(PrintWriter out, int indent) {\n    boolean first = true;\n    for (Pair<CppExpr, CppStmt> p : cases) {\n      CppExpr guard = p.fst();\n      CppStmt code = p.snd();\n      CodeGenUtil.printIndent(out, indent);\n      if (!first) {\n        out.print(\"} else \");\n      }\n      out.print(\"if (\");\n      guard.print(out);\n      out.println(\") {\");\n      code.println(out, indent + 1);\n      first = false;\n    }\n    CodeGenUtil.printIndent(out, indent);\n    if (elseBranch == null) {\n      out.println(\"}\");\n      return;\n    }\n    out.println(\"} else {\");\n    elseBranch.println(out, indent + 1);\n    CodeGenUtil.printIndent(out, indent);\n    out.println(\"}\");\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/cpp/CppLabel.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.cpp;\n\nimport edu.harvard.seas.pl.formulog.codegen.CodeGenUtil;\nimport java.io.PrintWriter;\n\npublic class CppLabel implements CppStmt {\n\n  private final String label;\n\n  public CppLabel(String label) {\n    this.label = label;\n  }\n\n  public static CppLabel mk(String label) {\n    return new CppLabel(label);\n  }\n\n  @Override\n  public void println(PrintWriter out, int indent) {\n    CodeGenUtil.printIndent(out, indent);\n    out.println(label + \":\");\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/cpp/CppMethodCall.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2022-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.cpp;\n\nimport edu.harvard.seas.pl.formulog.codegen.CodeGenUtil;\nimport java.io.PrintWriter;\nimport java.util.Arrays;\nimport java.util.List;\n\npublic class CppMethodCall implements CppExpr {\n\n  private final String func;\n  private final CppExpr rec;\n  private final List<CppExpr> args;\n  private final boolean thruPtr;\n\n  private CppMethodCall(CppExpr rec, String func, List<CppExpr> args, boolean thruPtr) {\n    this.func = func;\n    this.rec = rec;\n    this.args = args;\n    this.thruPtr = thruPtr;\n  }\n\n  public static CppMethodCall mk(CppExpr rec, String func, List<CppExpr> args) {\n    return new CppMethodCall(rec, func, args, false);\n  }\n\n  public static CppMethodCall mk(CppExpr rec, String func, CppExpr... args) {\n    return new CppMethodCall(rec, func, Arrays.asList(args), false);\n  }\n\n  public static CppMethodCall mkThruPtr(CppExpr rec, String func, List<CppExpr> args) {\n    return new CppMethodCall(rec, func, args, true);\n  }\n\n  public static CppMethodCall mkThruPtr(CppExpr rec, String func, CppExpr... args) {\n    return new CppMethodCall(rec, func, Arrays.asList(args), true);\n  }\n\n  @Override\n  public void print(PrintWriter out) {\n    rec.print(out);\n    out.print(thruPtr ? \"->\" : \".\");\n    out.print(func);\n    out.print(\"(\");\n    CodeGenUtil.printSeparated(args, \", \", out);\n    out.print(\")\");\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/cpp/CppNewArray.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.cpp;\n\nimport java.io.PrintWriter;\n\npublic class CppNewArray implements CppExpr {\n\n  private final String type;\n  private final CppExpr size;\n\n  private CppNewArray(String type, CppExpr size) {\n    this.type = type;\n    this.size = size;\n  }\n\n  public static CppNewArray mk(String type, CppExpr size) {\n    return new CppNewArray(type, size);\n  }\n\n  @Override\n  public void print(PrintWriter out) {\n    out.print(\"new \");\n    out.print(type);\n    out.print(\"[\");\n    size.print(out);\n    out.print(\"]\");\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/cpp/CppNullptr.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2022-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.cpp;\n\nimport java.io.PrintWriter;\n\npublic enum CppNullptr implements CppExpr {\n  INSTANCE;\n\n  @Override\n  public void print(PrintWriter out) {\n    out.print(\"nullptr\");\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/cpp/CppReturn.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.cpp;\n\nimport edu.harvard.seas.pl.formulog.codegen.CodeGenUtil;\nimport java.io.PrintWriter;\n\npublic class CppReturn implements CppStmt {\n\n  private final CppExpr val;\n\n  private CppReturn(CppExpr val) {\n    this.val = val;\n  }\n\n  public static CppReturn mk(CppExpr val) {\n    return new CppReturn(val);\n  }\n\n  public static CppReturn mk() {\n    return new CppReturn(null);\n  }\n\n  @Override\n  public void println(PrintWriter out, int indent) {\n    CodeGenUtil.printIndent(out, indent);\n    if (val == null) {\n      out.println(\"return;\");\n      return;\n    }\n    out.print(\"return \");\n    val.print(out);\n    out.println(\";\");\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/cpp/CppSeq.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.cpp;\n\nimport edu.harvard.seas.pl.formulog.codegen.CodeGenUtil;\nimport java.io.PrintWriter;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\npublic class CppSeq implements CppStmt {\n\n  private final List<CppStmt> stmts;\n\n  private CppSeq(List<CppStmt> stmts) {\n    this.stmts = stmts;\n  }\n\n  public static CppSeq mk(List<CppStmt> stmts) {\n    if (stmts.isEmpty()) {\n      return skip;\n    }\n    return new CppSeq(stmts);\n  }\n\n  public static CppSeq mk(CppStmt... stmts) {\n    return mk(Arrays.asList(stmts));\n  }\n\n  public static CppSeq skip() {\n    return skip;\n  }\n\n  private static final CppSeq skip = new CppSeq(Collections.emptyList());\n\n  @Override\n  public void println(PrintWriter out, int indent) {\n    CodeGenUtil.print(stmts, out, indent);\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/cpp/CppStmt.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.cpp;\n\nimport java.io.PrintWriter;\n\npublic interface CppStmt {\n\n  void println(PrintWriter out, int indent);\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/cpp/CppSubscript.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.cpp;\n\nimport java.io.PrintWriter;\n\npublic class CppSubscript implements CppExpr {\n\n  private final CppExpr val;\n  private final CppExpr idx;\n\n  private CppSubscript(CppExpr val, CppExpr idx) {\n    this.val = val;\n    this.idx = idx;\n  }\n\n  public static CppSubscript mk(CppExpr val, CppExpr idx) {\n    return new CppSubscript(val, idx);\n  }\n\n  @Override\n  public void print(PrintWriter out) {\n    val.print(out);\n    out.print(\"[\");\n    idx.print(out);\n    out.print(\"]\");\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/cpp/CppUnop.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.cpp;\n\nimport java.io.PrintWriter;\n\npublic class CppUnop implements CppExpr {\n\n  private final String op;\n  private final CppExpr expr;\n\n  private CppUnop(String op, CppExpr expr) {\n    this.op = op;\n    this.expr = expr;\n  }\n\n  private static CppUnop mk(String op, CppExpr expr) {\n    return new CppUnop(op, expr);\n  }\n\n  public static CppUnop mkNot(CppExpr expr) {\n    return mk(\"!\", expr);\n  }\n\n  public static CppUnop mkPreIncr(CppExpr expr) {\n    return mk(\"++\", expr);\n  }\n\n  public static CppUnop mkDeref(CppExpr expr) {\n    return mk(\"*\", expr);\n  }\n\n  @Override\n  public void print(PrintWriter out) {\n    out.print(\"(\");\n    out.print(op);\n    expr.print(out);\n    out.print(\")\");\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/cpp/CppVar.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.cpp;\n\nimport java.io.PrintWriter;\n\npublic class CppVar implements CppExpr {\n\n  private final String var;\n\n  private CppVar(String var) {\n    this.var = var;\n  }\n\n  public static CppVar mk(String var) {\n    return new CppVar(var);\n  }\n\n  @Override\n  public String toString() {\n    return var;\n  }\n\n  @Override\n  public void print(PrintWriter out) {\n    out.print(var);\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/cpp/CppVectorLiteral.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2022-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.cpp;\n\nimport java.io.PrintWriter;\nimport java.util.List;\n\npublic class CppVectorLiteral implements CppExpr {\n\n  private final List<CppExpr> elts;\n\n  private CppVectorLiteral(List<CppExpr> elts) {\n    this.elts = elts;\n  }\n\n  public static CppVectorLiteral mk(List<CppExpr> elts) {\n    return new CppVectorLiteral(elts);\n  }\n\n  @Override\n  public void print(PrintWriter out) {\n    out.print(\"{\");\n    for (int i = 0; i < elts.size(); ++i) {\n      elts.get(i).print(out);\n      if (i < elts.size() - 1) {\n        out.print(\", \");\n      }\n    }\n    out.print(\"}\");\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/cpp/CppWhile.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.cpp;\n\nimport edu.harvard.seas.pl.formulog.codegen.CodeGenUtil;\nimport java.io.PrintWriter;\n\npublic class CppWhile implements CppStmt {\n\n  private final CppExpr guard;\n  private final CppStmt body;\n\n  private CppWhile(CppExpr guard, CppStmt body) {\n    this.guard = guard;\n    this.body = body;\n  }\n\n  public static CppWhile mk(CppExpr guard, CppStmt body) {\n    return new CppWhile(guard, body);\n  }\n\n  @Override\n  public void println(PrintWriter out, int indent) {\n    CodeGenUtil.printIndent(out, indent);\n    out.print(\"while (\");\n    guard.print(out);\n    out.println(\") {\");\n    body.println(out, indent + 1);\n    CodeGenUtil.printIndent(out, indent);\n    out.println(\"}\");\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/souffle/SAtom.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2022-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.souffle;\n\nimport java.util.List;\nimport java.util.Set;\n\npublic class SAtom implements SLit {\n\n  private final String pred;\n  private final List<STerm> args;\n  private final boolean isNegated;\n\n  public SAtom(String symbol, List<STerm> args, boolean isNegated) {\n    pred = symbol;\n    this.args = args;\n    this.isNegated = isNegated;\n  }\n\n  public String getSymbol() {\n    return pred;\n  }\n\n  @Override\n  public String toString() {\n    StringBuilder sb = new StringBuilder();\n    if (isNegated) {\n      sb.append(\"!\");\n    }\n    sb.append(pred);\n    sb.append(\"(\");\n    for (int i = 0; i < args.size(); ++i) {\n      sb.append(args.get(i));\n      if (i < args.size() - 1) {\n        sb.append(\", \");\n      }\n    }\n    sb.append(\")\");\n    return sb.toString();\n  }\n\n  @Override\n  public void varSet(Set<SVar> vars) {\n    for (var t : args) {\n      t.varSet(vars);\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/souffle/SDestructorBody.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2022-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.souffle;\n\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.ast.Var;\nimport edu.harvard.seas.pl.formulog.symbols.ConstructorSymbol;\nimport java.util.List;\n\npublic class SDestructorBody implements SFunctorBody {\n\n  private final List<Var> args;\n  private final Term scrutinee;\n  private final ConstructorSymbol symbol;\n\n  public SDestructorBody(List<Var> args_, Term scrutinee_, ConstructorSymbol symbol_) {\n    args = args_;\n    scrutinee = scrutinee_;\n    symbol = symbol_;\n  }\n\n  @Override\n  public List<Var> getArgs() {\n    return args;\n  }\n\n  @Override\n  public SType getRetType() {\n    return SIntType.INSTANCE;\n  }\n\n  @Override\n  public boolean isStateful() {\n    return false;\n  }\n\n  public Term getScrutinee() {\n    return scrutinee;\n  }\n\n  public ConstructorSymbol getSymbol() {\n    return symbol;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/souffle/SExprBody.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2022-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.souffle;\n\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.ast.Var;\nimport java.util.List;\n\npublic class SExprBody implements SFunctorBody {\n\n  private final List<Var> args;\n  private final Term body;\n\n  public SExprBody(List<Var> args_, Term body_) {\n    args = args_;\n    body = body_;\n  }\n\n  public Term getBody() {\n    return body;\n  }\n\n  @Override\n  public List<Var> getArgs() {\n    return args;\n  }\n\n  @Override\n  public SType getRetType() {\n    return SIntType.INSTANCE;\n  }\n\n  @Override\n  public boolean isStateful() {\n    return false;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/souffle/SFunctorBody.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2022-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.souffle;\n\nimport edu.harvard.seas.pl.formulog.ast.Var;\nimport java.util.List;\n\npublic interface SFunctorBody {\n\n  default int getArity() {\n    return getArgs().size();\n  }\n\n  List<Var> getArgs();\n\n  SType getRetType();\n\n  boolean isStateful();\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/souffle/SFunctorCall.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2022-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.souffle;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Set;\n\npublic class SFunctorCall implements STerm {\n\n  private final String func;\n  private final List<STerm> args;\n\n  public SFunctorCall(String func, List<STerm> args) {\n    this.func = func;\n    this.args = args;\n  }\n\n  public SFunctorCall(String func, STerm... args) {\n    this(func, Arrays.asList(args));\n  }\n\n  @Override\n  public String toString() {\n    StringBuilder sb = new StringBuilder();\n    sb.append(\"@\");\n    sb.append(func);\n    sb.append(\"(\");\n    for (int i = 0; i < args.size(); ++i) {\n      sb.append(args.get(i));\n      if (i < args.size() - 1) {\n        sb.append(\", \");\n      }\n    }\n    sb.append(\")\");\n    return sb.toString();\n  }\n\n  @Override\n  public void varSet(Set<SVar> vars) {\n    for (var t : args) {\n      t.varSet(vars);\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/souffle/SInfixBinaryOpAtom.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2022-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.souffle;\n\nimport java.util.Set;\n\npublic class SInfixBinaryOpAtom implements SLit {\n\n  private final STerm lhs;\n  private final String op;\n  private final STerm rhs;\n\n  public SInfixBinaryOpAtom(STerm lhs_, String op_, STerm rhs_) {\n    lhs = lhs_;\n    op = op_;\n    rhs = rhs_;\n  }\n\n  @Override\n  public String toString() {\n    return lhs + \" \" + op + \" \" + rhs;\n  }\n\n  @Override\n  public void varSet(Set<SVar> vars) {\n    lhs.varSet(vars);\n    rhs.varSet(vars);\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/souffle/SInt.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2022-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.souffle;\n\nimport java.util.Set;\n\npublic class SInt implements STerm {\n\n  private final int val;\n\n  public SInt(int val_) {\n    val = val_;\n  }\n\n  @Override\n  public String toString() {\n    return Integer.toString(val);\n  }\n\n  @Override\n  public void varSet(Set<SVar> vars) {\n    // do nothing\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/souffle/SIntList.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2022-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.souffle;\n\nimport java.util.List;\nimport java.util.Set;\n\npublic class SIntList implements STerm {\n\n  private final List<STerm> ts;\n\n  public SIntList(List<STerm> ts_) {\n    ts = ts_;\n  }\n\n  @Override\n  public String toString() {\n    StringBuilder sb = new StringBuilder();\n    sb.append(\"[\");\n    for (int i = 0; i < ts.size(); ++i) {\n      sb.append(ts.get(i));\n      if (i < ts.size() - 1) {\n        sb.append(\", \");\n      }\n    }\n    sb.append(\"]\");\n    return sb.toString();\n  }\n\n  @Override\n  public void varSet(Set<SVar> vars) {\n    for (var t : ts) {\n      t.varSet(vars);\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/souffle/SIntListType.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2022-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.souffle;\n\nimport java.util.Objects;\n\npublic class SIntListType implements SType {\n\n  private final int arity;\n\n  public SIntListType(int arity) {\n    this.arity = arity;\n  }\n\n  public String getName() {\n    return \"IntList\" + arity;\n  }\n\n  public String getDef() {\n    StringBuilder sb = new StringBuilder();\n    sb.append(\"[\");\n    for (int i = 0; i < arity; ++i) {\n      sb.append(\"x\");\n      sb.append(i);\n      sb.append(\":number\");\n      if (i < arity - 1) {\n        sb.append(\", \");\n      }\n    }\n    sb.append(\"]\");\n    return sb.toString();\n  }\n\n  @Override\n  public boolean equals(Object o) {\n    if (this == o) return true;\n    if (o == null || getClass() != o.getClass()) return false;\n    SIntListType that = (SIntListType) o;\n    return arity == that.arity;\n  }\n\n  @Override\n  public int hashCode() {\n    return Objects.hash(arity);\n  }\n\n  @Override\n  public String toString() {\n    return getName();\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/souffle/SIntType.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2022-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.souffle;\n\npublic enum SIntType implements SType {\n  INSTANCE;\n\n  @Override\n  public String toString() {\n    return \"number\";\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/souffle/SLit.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2022-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.souffle;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\npublic interface SLit {\n\n  default Set<SVar> varSet() {\n    Set<SVar> vars = new HashSet<>();\n    varSet(vars);\n    return vars;\n  }\n\n  void varSet(Set<SVar> vars);\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/souffle/SRule.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2022-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.souffle;\n\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\npublic class SRule {\n\n  private final SLit head;\n  private final List<SLit> body;\n  private List<Pair<Integer, int[]>> queryPlan;\n\n  public SRule(SLit head, List<SLit> body) {\n    this.head = head;\n    this.body = body;\n  }\n\n  public SRule(SLit head, SLit... body) {\n    this(head, Arrays.asList(body));\n  }\n\n  public List<SLit> getBody() {\n    return Collections.unmodifiableList(body);\n  }\n\n  public void setQueryPlan(List<Pair<Integer, int[]>> queryPlan) {\n    this.queryPlan = queryPlan;\n  }\n\n  @Override\n  public String toString() {\n    StringBuilder sb = new StringBuilder();\n    sb.append(head);\n    if (!body.isEmpty()) {\n      sb.append(\" :- \");\n      String sep = \"\";\n      if (body.size() > 1) {\n        sep = \"\\n\\t\";\n      }\n      for (int i = 0; i < body.size(); ++i) {\n        sb.append(sep);\n        sb.append(body.get(i));\n        if (i < body.size() - 1) {\n          sb.append(\", \");\n        }\n      }\n    }\n    sb.append(\".\");\n    if (queryPlan != null) {\n      sb.append(\"\\n\");\n      for (int i = 0; i < queryPlan.size(); ++i) {\n        if (i == 0) {\n          sb.append(\".plan \");\n        } else {\n          sb.append(\", \");\n        }\n        var p = queryPlan.get(i);\n        sb.append(p.fst());\n        sb.append(\": (\");\n        var plan = p.snd();\n        for (int j = 0; j < plan.length; ++j) {\n          sb.append(plan[j]);\n          if (j < plan.length - 1) {\n            sb.append(\",\");\n          }\n        }\n        sb.append(\")\");\n      }\n    }\n    return sb.toString();\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/souffle/SRuleMode.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2022-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.souffle;\n\npublic enum SRuleMode {\n  INPUT,\n  OUTPUT,\n  INTERMEDIATE;\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/souffle/STerm.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2022-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.souffle;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\npublic interface STerm {\n\n  default Set<SVar> varSet() {\n    Set<SVar> vars = new HashSet<>();\n    varSet(vars);\n    return vars;\n  }\n\n  void varSet(Set<SVar> vars);\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/souffle/SType.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2022-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.souffle;\n\npublic interface SType {}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/codegen/ast/souffle/SVar.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2022-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen.ast.souffle;\n\nimport edu.harvard.seas.pl.formulog.ast.Var;\nimport java.util.Set;\n\npublic class SVar implements STerm {\n\n  private final String name;\n\n  public SVar(Var var) {\n    String s = var.toString();\n    if (s.charAt(0) == '$') {\n      s = \"x\" + s.substring(1);\n    }\n    name = s;\n  }\n\n  @Override\n  public String toString() {\n    return name;\n  }\n\n  @Override\n  public void varSet(Set<SVar> vars) {\n    vars.add(this);\n  }\n\n  @Override\n  public int hashCode() {\n    final int prime = 31;\n    int result = 1;\n    result = prime * result + ((name == null) ? 0 : name.hashCode());\n    return result;\n  }\n\n  @Override\n  public boolean equals(Object obj) {\n    if (this == obj) return true;\n    if (obj == null) return false;\n    if (getClass() != obj.getClass()) return false;\n    SVar other = (SVar) obj;\n    if (name == null) {\n      if (other.name != null) return false;\n    } else if (!name.equals(other.name)) return false;\n    return true;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/db/BindingTypeArrayWrapper.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.db;\n\nimport edu.harvard.seas.pl.formulog.ast.BindingType;\nimport java.util.Arrays;\n\npublic class BindingTypeArrayWrapper {\n  private final BindingType[] arr;\n\n  public BindingTypeArrayWrapper(BindingType[] arr) {\n    this.arr = arr;\n  }\n\n  public BindingType[] getArr() {\n    return arr;\n  }\n\n  @Override\n  public int hashCode() {\n    final int prime = 31;\n    int result = 1;\n    result = prime * result + Arrays.hashCode(arr);\n    return result;\n  }\n\n  @Override\n  public boolean equals(Object obj) {\n    if (this == obj) return true;\n    if (obj == null) return false;\n    if (getClass() != obj.getClass()) return false;\n    BindingTypeArrayWrapper other = (BindingTypeArrayWrapper) obj;\n    if (!Arrays.equals(arr, other.arr)) return false;\n    return true;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/db/ExampleComparator.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.db;\n\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport java.util.Comparator;\n\npublic class ExampleComparator implements Comparator<Term[]> {\n\n  @Override\n  public int compare(Term[] xs, Term[] ys) {\n    int xid = xs[0].getId();\n    int yid = ys[0].getId();\n    if (xid < yid) {\n      return -1;\n    } else if (xid > yid) {\n      return 1;\n    }\n\n    xid = xs[2].getId();\n    yid = ys[2].getId();\n    if (xid < yid) {\n      return -1;\n    } else if (xid > yid) {\n      return 1;\n    }\n\n    xid = xs[1].getId();\n    yid = ys[1].getId();\n    if (xid < yid) {\n      return -1;\n    } else if (xid > yid) {\n      return 1;\n    }\n    return 0;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/db/IndexedFactDb.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.db;\n\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.symbols.RelationSymbol;\nimport java.util.Set;\n\npublic interface IndexedFactDb {\n\n  Set<RelationSymbol> getSymbols();\n\n  Iterable<Term[]> getAll(RelationSymbol sym);\n\n  boolean isEmpty(RelationSymbol sym);\n\n  int countDistinct(RelationSymbol sym);\n\n  int numIndices(RelationSymbol sym);\n\n  int countDuplicates(RelationSymbol sym);\n\n  Iterable<Term[]> get(RelationSymbol sym, Term[] key, int index);\n\n  boolean add(RelationSymbol sym, Term[] args);\n\n  boolean addAll(RelationSymbol sym, Iterable<Term[]> tups);\n\n  boolean hasFact(RelationSymbol sym, Term[] args);\n\n  void clear();\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/db/IndexedFactDbBuilder.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.db;\n\nimport edu.harvard.seas.pl.formulog.ast.BindingType;\nimport edu.harvard.seas.pl.formulog.symbols.RelationSymbol;\n\npublic interface IndexedFactDbBuilder<T extends IndexedFactDb> {\n\n  int makeIndex(RelationSymbol sym, BindingType[] pat);\n\n  T build();\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/db/MinChainCover.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2020-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.db;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.BiFunction;\nimport org.jgrapht.Graph;\nimport org.jgrapht.alg.flow.PushRelabelMFImpl;\nimport org.jgrapht.alg.interfaces.MaximumFlowAlgorithm;\nimport org.jgrapht.graph.SimpleDirectedGraph;\n\npublic class MinChainCover<T> {\n\n  private final BiFunction<T, T, Boolean> lessThan;\n\n  public MinChainCover(BiFunction<T, T, Boolean> lessThan) {\n    this.lessThan = lessThan;\n  }\n\n  public Iterable<Iterable<T>> compute(Set<T> elts) {\n    return new Worker(elts).go();\n  }\n\n  private class Worker {\n\n    private final Graph<Node, Edge> bigraph = new SimpleDirectedGraph<>(Edge.class);\n    private final T[] elts;\n    private final Boolean[][] memo;\n\n    private final Node source =\n        new Node() {\n\n          @Override\n          public String toString() {\n            return \"source\";\n          }\n        };\n\n    private final Node sink =\n        new Node() {\n\n          @Override\n          public String toString() {\n            return \"sink\";\n          }\n        };\n\n    @SuppressWarnings(\"unchecked\")\n    public Worker(Set<T> s) {\n      int n = s.size();\n      elts = (T[]) new Object[n];\n      int i = 0;\n      for (T elt : s) {\n        elts[i] = elt;\n        ++i;\n      }\n      memo = new Boolean[n][n];\n    }\n\n    public Iterable<Iterable<T>> go() {\n      mkBipartiteGraph();\n      return mkChains();\n    }\n\n    private void mkBipartiteGraph() {\n      bigraph.addVertex(source);\n      bigraph.addVertex(sink);\n      for (T elt : elts) {\n        Node left = new InnerNode(elt, false);\n        Node right = new InnerNode(elt, true);\n        bigraph.addVertex(left);\n        bigraph.addVertex(right);\n        bigraph.addEdge(source, left, new Edge(source, left));\n        bigraph.addEdge(right, sink, new Edge(right, sink));\n      }\n      for (int i = 0; i < elts.length; ++i) {\n        Node iv = new InnerNode(elts[i], false);\n        for (int j = 0; j < elts.length; ++j) {\n          considerEdge(i, iv, j);\n        }\n      }\n    }\n\n    private void considerEdge(int i, Node iv, int j) {\n      if (memo[i][j] == null) {\n        memoize(i, j);\n      }\n      if (memo[i][j]) {\n        Node jv = new InnerNode(elts[j], true);\n        bigraph.addEdge(iv, jv, new Edge(iv, jv));\n      }\n    }\n\n    private void memoize(int i, int j) {\n      if (i == j) {\n        memo[i][j] = false;\n        return;\n      }\n      if (lessThan.apply(elts[i], elts[j])) {\n        memo[i][j] = true;\n        for (int k = 0; k < elts.length; ++k) {\n          Boolean b = memo[j][k];\n          if (b != null && b) {\n            memo[i][k] = true;\n          }\n        }\n      } else {\n        memo[i][j] = false;\n      }\n    }\n\n    private Map<Edge, Double> computeMaxFlowMap() {\n      MaximumFlowAlgorithm<Node, Edge> maxFlow = new PushRelabelMFImpl<>(bigraph);\n      return maxFlow.getMaximumFlow(source, sink).getFlowMap();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private Iterable<Iterable<T>> mkChains() {\n      Map<Edge, Double> maxFlowMap = computeMaxFlowMap();\n      Set<T> roots = new HashSet<>(Arrays.asList(elts));\n      Map<T, T> m = new HashMap<>();\n      for (Map.Entry<Edge, Double> entry : maxFlowMap.entrySet()) {\n        if (entry.getValue() > 0) {\n          Edge edge = entry.getKey();\n          if (edge.src == source || edge.dst == sink) {\n            continue;\n          }\n          InnerNode src = (InnerNode) edge.src;\n          InnerNode dst = (InnerNode) edge.dst;\n          roots.remove(dst.elt);\n          T other = m.put(src.elt, dst.elt);\n          assert other == null;\n        }\n      }\n      List<Iterable<T>> chains = new ArrayList<>();\n      for (T root : roots) {\n        T cur = root;\n        List<T> chain = new ArrayList<>();\n        while (cur != null) {\n          chain.add(cur);\n          cur = m.get(cur);\n        }\n        chains.add(chain);\n      }\n      return chains;\n    }\n  }\n\n  private static interface Node {}\n\n  private class InnerNode implements Node {\n\n    public final T elt;\n    public final boolean side;\n\n    public InnerNode(T elt, boolean side) {\n      this.elt = elt;\n      this.side = side;\n    }\n\n    @Override\n    public int hashCode() {\n      final int prime = 31;\n      int result = 1;\n      result = prime * result + ((elt == null) ? 0 : elt.hashCode());\n      result = prime * result + (side ? 1231 : 1237);\n      return result;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n      if (this == obj) return true;\n      if (obj == null) return false;\n      if (getClass() != obj.getClass()) return false;\n      @SuppressWarnings(\"unchecked\")\n      InnerNode other = (InnerNode) obj;\n      if (elt == null) {\n        if (other.elt != null) return false;\n      } else if (!elt.equals(other.elt)) return false;\n      if (side != other.side) return false;\n      return true;\n    }\n\n    @Override\n    public String toString() {\n      return elt + ((side) ? \"@R\" : \"@L\");\n    }\n  }\n\n  private static class Edge {\n\n    public final Node src;\n    public final Node dst;\n\n    public Edge(Node src, Node dst) {\n      this.src = src;\n      this.dst = dst;\n    }\n\n    @Override\n    public int hashCode() {\n      final int prime = 31;\n      int result = 1;\n      result = prime * result + ((dst == null) ? 0 : dst.hashCode());\n      result = prime * result + ((src == null) ? 0 : src.hashCode());\n      return result;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n      if (this == obj) return true;\n      if (obj == null) return false;\n      if (getClass() != obj.getClass()) return false;\n      Edge other = (Edge) obj;\n      if (dst == null) {\n        if (other.dst != null) return false;\n      } else if (!dst.equals(other.dst)) return false;\n      if (src == null) {\n        if (other.src != null) return false;\n      } else if (!src.equals(other.src)) return false;\n      return true;\n    }\n\n    @Override\n    public String toString() {\n      return src + \" ---> \" + dst;\n    }\n  }\n\n  public static void main(String[] args) {\n    Set<String> s1 = new HashSet<>(Arrays.asList(\"x\"));\n    Set<String> s2 = new HashSet<>(Arrays.asList(\"x\", \"y\"));\n    Set<String> s3 = new HashSet<>(Arrays.asList(\"x\", \"z\"));\n    Set<String> s4 = new HashSet<>(Arrays.asList(\"x\", \"y\", \"z\"));\n    Set<Set<String>> elts = new HashSet<>(Arrays.asList(s1, s2, s3, s4));\n    MinChainCover<Set<String>> mcc = new MinChainCover<>((x, y) -> y.containsAll(x));\n    System.out.println(mcc.compute(elts));\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/db/MinIndex.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2020-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.db;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\npublic final class MinIndex {\n\n  private MinIndex() {\n    throw new AssertionError(\"impossible\");\n  }\n\n  public static <T> Map<Set<T>, Iterable<T>> compute(Set<T> domain, Set<Set<T>> searches) {\n    MinChainCover<Set<T>> mcc = new MinChainCover<>((x, y) -> y.containsAll(x));\n    Iterable<Iterable<Set<T>>> chains = mcc.compute(searches);\n    Map<Set<T>, Iterable<T>> idxs = new HashMap<>();\n    for (Iterable<Set<T>> chain : chains) {\n      List<T> idx = computeIndex(chain);\n      padIndex(idx, domain);\n      for (Set<T> search : chain) {\n        Iterable<T> other = idxs.put(search, idx);\n        assert other == null;\n      }\n    }\n    return idxs;\n  }\n\n  private static <T> void padIndex(List<T> idx, Set<T> domain) {\n    Set<T> seen = new HashSet<>(idx);\n    for (T elt : domain) {\n      if (seen.add(elt)) {\n        idx.add(elt);\n      }\n    }\n  }\n\n  private static <T> List<T> computeIndex(Iterable<Set<T>> chain) {\n    List<T> index = new ArrayList<>();\n    Set<T> prev = Collections.emptySet();\n    for (Set<T> s : chain) {\n      for (T elt : s) {\n        if (!prev.contains(elt)) {\n          index.add(elt);\n        }\n      }\n      prev = s;\n    }\n    return index;\n  }\n\n  public static void main(String[] args) {\n    Set<String> s1 = new HashSet<>(Arrays.asList(\"x\"));\n    Set<String> s2 = new HashSet<>(Arrays.asList(\"x\", \"y\"));\n    Set<String> s3 = new HashSet<>(Arrays.asList(\"x\", \"z\"));\n    Set<String> s4 = new HashSet<>(Arrays.asList(\"x\", \"y\", \"z\"));\n    Set<Set<String>> searches = new HashSet<>(Arrays.asList(s1, s2, s3, s4));\n    System.out.println(MinIndex.compute(s4, searches));\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/db/SortedIndexedFactDb.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.db;\n\nimport edu.harvard.seas.pl.formulog.Configuration;\nimport edu.harvard.seas.pl.formulog.ast.BindingType;\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.ast.Terms;\nimport edu.harvard.seas.pl.formulog.symbols.RelationSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.SymbolComparator;\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport java.lang.reflect.InvocationTargetException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.NavigableSet;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentSkipListSet;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.stream.Collectors;\n\npublic class SortedIndexedFactDb implements IndexedFactDb {\n\n  private final Map<RelationSymbol, Pair<IndexedFactSet, BindingType[]>[]> indices;\n  private final Map<RelationSymbol, Pair<IndexedFactSet, BindingType[]>> masterIndex;\n\n  private final Map<RelationSymbol, Set<HashedTuple>> hashFilter = new HashMap<>();\n  private final ThreadLocal<HashedTuple> hashKey = ThreadLocal.withInitial(HashedTuple::new);\n\n  private SortedIndexedFactDb(\n      Map<RelationSymbol, Pair<IndexedFactSet, BindingType[]>[]> indices,\n      Map<RelationSymbol, Pair<IndexedFactSet, BindingType[]>> masterIndex) {\n    this.indices = indices;\n    this.masterIndex = masterIndex;\n    for (var sym : indices.keySet()) {\n      hashFilter.put(sym, Util.concurrentSet());\n    }\n  }\n\n  private static class HashedTuple {\n    public Term[] tup;\n\n    @Override\n    public int hashCode() {\n      return Arrays.hashCode(tup);\n    }\n\n    @Override\n    public boolean equals(Object other) {\n      if (other instanceof HashedTuple) {\n        return Arrays.equals(tup, ((HashedTuple) other).tup);\n      }\n      return false;\n    }\n  }\n\n  @Override\n  public Set<RelationSymbol> getSymbols() {\n    return Collections.unmodifiableSet(masterIndex.keySet());\n  }\n\n  @Override\n  public Iterable<Term[]> getAll(RelationSymbol sym) {\n    return masterIndex.get(sym).fst().getAll();\n  }\n\n  @Override\n  public boolean isEmpty(RelationSymbol sym) {\n    return masterIndex.get(sym).fst().isEmpty();\n  }\n\n  @Override\n  public int countDistinct(RelationSymbol sym) {\n    return masterIndex.get(sym).fst().count();\n  }\n\n  @Override\n  public int countDuplicates(RelationSymbol sym) {\n    int count = 0;\n    for (IndexedFactSet idx : getUniqueIndices(sym)) {\n      count += idx.count();\n    }\n    return count;\n  }\n\n  private Set<IndexedFactSet> getUniqueIndices(RelationSymbol sym) {\n    Set<IndexedFactSet> s = new HashSet<>();\n    for (Pair<IndexedFactSet, ?> e : indices.get(sym)) {\n      s.add(e.fst());\n    }\n    return s;\n  }\n\n  @Override\n  public Iterable<Term[]> get(RelationSymbol sym, Term[] key, int index) {\n    Pair<IndexedFactSet, BindingType[]> p = indices.get(sym)[index];\n    return p.fst().lookup(key, p.snd());\n  }\n\n  private boolean hashFilterAdd(RelationSymbol sym, Term[] tup) {\n    var key = hashKey.get();\n    key.tup = tup;\n    if (hashFilter.get(sym).add(key)) {\n      hashKey.set(new HashedTuple());\n      return true;\n    }\n    key.tup = null;\n    return false;\n  }\n\n  private boolean hashFilterContains(RelationSymbol sym, Term[] tup) {\n    var key = hashKey.get();\n    key.tup = tup;\n    var r = hashFilter.get(sym).contains(key);\n    key.tup = null;\n    return r;\n  }\n\n  @Override\n  public boolean add(RelationSymbol sym, Term[] tup) {\n    assert allNormal(tup);\n    if (Configuration.useHashDbFilter && !hashFilterAdd(sym, tup)) {\n      return false;\n    }\n    IndexedFactSet master = masterIndex.get(sym).fst();\n    if (master.add(tup)) {\n      for (Pair<IndexedFactSet, ?> p : indices.get(sym)) {\n        IndexedFactSet idx = p.fst();\n        if (!idx.equals(master)) {\n          idx.add(tup);\n        }\n      }\n      return true;\n    }\n    return false;\n  }\n\n  @Override\n  public boolean addAll(RelationSymbol sym, Iterable<Term[]> tups) {\n    if (Configuration.useHashDbFilter) {\n      ArrayList<Term[]> l = new ArrayList<>();\n      for (var tup : tups) {\n        if (hashFilterAdd(sym, tup)) {\n          l.add(tup);\n        }\n      }\n      tups = l;\n    }\n    IndexedFactSet master = masterIndex.get(sym).fst();\n    if (master.addAll(tups)) {\n      for (Pair<IndexedFactSet, ?> p : indices.get(sym)) {\n        IndexedFactSet idx = p.fst();\n        if (!idx.equals(master)) {\n          idx.addAll(tups);\n        }\n      }\n      return true;\n    }\n    return false;\n  }\n\n  private boolean allNormal(Term[] args) {\n    for (Term arg : args) {\n      if (!arg.isGround() || arg.containsUnevaluatedTerm()) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  @Override\n  public boolean hasFact(RelationSymbol sym, Term[] args) {\n    assert allNormal(args);\n    if (Configuration.useHashDbFilter) {\n      return hashFilterContains(sym, args);\n    }\n    return masterIndex.get(sym).fst().contains(args);\n  }\n\n  @Override\n  public void clear() {\n    for (var s : hashFilter.values()) {\n      s.clear();\n    }\n    for (Pair<IndexedFactSet, BindingType[]>[] idxs : indices.values()) {\n      for (Pair<IndexedFactSet, ?> p : idxs) {\n        p.fst().clear();\n      }\n    }\n  }\n\n  @Override\n  public String toString() {\n    String s = \"{\\n\";\n    for (RelationSymbol sym : masterIndex.keySet()) {\n      s += \"\\t\" + sym + \" = {\\n\";\n      for (Pair<IndexedFactSet, BindingType[]> p : indices.get(sym)) {\n        s += \"\\t\" + Arrays.toString(p.snd()) + \"\\n\";\n        s += p.fst().toString() + \"\\n\";\n      }\n      s += \"\\t}\\n\";\n    }\n    return s + \"}\";\n  }\n\n  public String toSimplifiedString() {\n    String s = \"{\\n\";\n    for (RelationSymbol sym : masterIndex.keySet()) {\n      Pair<IndexedFactSet, BindingType[]> p = masterIndex.get(sym);\n      IndexedFactSet idx = p.fst();\n      if (!idx.isEmpty()) {\n        s += \"\\t\" + sym + \" = {\\n\";\n        s += \"\\t\" + Arrays.toString(p.snd()) + \"\\n\";\n        s += idx.toString() + \"\\n\";\n        s += \"\\t}\\n\";\n      }\n    }\n    return s + \"}\";\n  }\n\n  @Override\n  public int numIndices(RelationSymbol sym) {\n    if (!indices.containsKey(sym)) {\n      throw new IllegalArgumentException(\"Unrecognized symbol: \" + sym);\n    }\n    return getUniqueIndices(sym).size();\n  }\n\n  public IndexInfo getIndexInfo(RelationSymbol sym, int idx) {\n    if (idx < 0 || idx > numIndices(sym)) {\n      throw new IllegalArgumentException(\"Unrecognized index for symbol \" + sym + \": \" + idx);\n    }\n    Pair<IndexedFactSet, BindingType[]> p = indices.get(sym)[idx];\n    IndexedFactSet index = p.fst();\n    return new IndexInfo(index.getId(), index.comparatorOrder, p.snd());\n  }\n\n  public int getMasterIndex(RelationSymbol sym) {\n    if (!indices.containsKey(sym)) {\n      throw new IllegalArgumentException(\"Unrecognized symbol: \" + sym);\n    }\n    int i = 0;\n    IndexedFactSet master = masterIndex.get(sym).fst();\n    for (Pair<IndexedFactSet, ?> p : indices.get(sym)) {\n      if (p.fst().equals(master)) {\n        break;\n      }\n      i++;\n    }\n    return i;\n  }\n\n  public static class SortedIndexedFactDbBuilder\n      implements IndexedFactDbBuilder<SortedIndexedFactDb> {\n\n    private final Map<RelationSymbol, Integer> counts = new HashMap<>();\n    private final Map<RelationSymbol, Map<BindingTypeArrayWrapper, Integer>> pats =\n        new LinkedHashMap<>();\n    private final Map<RelationSymbol, Pair<IndexedFactSet, BindingType[]>> masterIndex =\n        new HashMap<>();\n\n    public SortedIndexedFactDbBuilder(Set<RelationSymbol> allSyms) {\n      List<RelationSymbol> sortedSyms =\n          allSyms.stream().sorted(SymbolComparator.INSTANCE).collect(Collectors.toList());\n      for (RelationSymbol sym : sortedSyms) {\n        pats.put(sym, new HashMap<>());\n        counts.put(sym, 0);\n      }\n    }\n\n    @Override\n    public synchronized int makeIndex(RelationSymbol sym, BindingType[] pat) {\n      assert sym.getArity() == pat.length;\n      Map<BindingTypeArrayWrapper, Integer> m = pats.get(sym);\n      BindingTypeArrayWrapper key = new BindingTypeArrayWrapper(pat);\n      assert m != null : \"Symbol not registered with DB: \" + sym;\n      Integer idx = m.get(key);\n      if (idx == null) {\n        idx = counts.get(sym);\n        counts.put(sym, idx + 1);\n        m.put(key, idx);\n      }\n      return idx;\n    }\n\n    @Override\n    public SortedIndexedFactDb build() {\n      Map<RelationSymbol, Pair<IndexedFactSet, BindingType[]>[]> indices = new HashMap<>();\n      for (Map.Entry<RelationSymbol, Map<BindingTypeArrayWrapper, Integer>> e : pats.entrySet()) {\n        RelationSymbol sym = e.getKey();\n        indices.put(sym, mkIndices(sym, e.getValue()));\n      }\n\n      List<RelationSymbol> sortedSyms =\n          counts.keySet().stream().sorted(SymbolComparator.INSTANCE).collect(Collectors.toList());\n      HashMap<RelationSymbol, Pair<IndexedFactSet, BindingType[]>> sorted = new LinkedHashMap<>();\n      for (RelationSymbol sym : sortedSyms) {\n        sorted.put(sym, masterIndex.get(sym));\n      }\n      return new SortedIndexedFactDb(indices, sorted);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private Pair<IndexedFactSet, BindingType[]>[] mkIndices(\n        RelationSymbol sym, Map<BindingTypeArrayWrapper, Integer> m) {\n      List<Pair<IndexedFactSet, BindingType[]>> indices;\n      if (Configuration.minIndex) {\n        indices = mkMinIndices(sym, m);\n      } else {\n        indices = mkNaiveIndices(sym, m);\n      }\n      boolean ok = false;\n      for (Pair<IndexedFactSet, BindingType[]> p : indices) {\n        IndexedFactSet idx = p.fst();\n        if (idx.comparatorLength() == sym.getArity()) {\n          masterIndex.put(sym, new Pair<>(idx, p.snd()));\n          ok = true;\n          break;\n        }\n      }\n      if (!ok) {\n        BindingType[] pat = new BindingType[sym.getArity()];\n        for (int i = 0; i < pat.length; ++i) {\n          pat[i] = BindingType.FREE;\n        }\n        IndexedFactSet master = IndexedFactSet.make(pat);\n        Pair<IndexedFactSet, BindingType[]> p = new Pair<>(master, pat);\n        masterIndex.put(sym, p);\n        indices.add(p);\n      }\n      assert indicesWellFormed(indices) : \"Bad index created for relation: \" + sym;\n      return indices.toArray(new Pair[0]);\n    }\n\n    private static boolean indicesWellFormed(\n        Iterable<Pair<IndexedFactSet, BindingType[]>> indices) {\n      boolean ok = true;\n      for (Pair<IndexedFactSet, BindingType[]> p : indices) {\n        ok &= p.fst().comparatorLength() == countNumNotIgnored(p.snd());\n      }\n      return ok;\n    }\n\n    private static int countNumNotIgnored(BindingType[] pat) {\n      int i = 0;\n      for (BindingType b : pat) {\n        if (!b.isIgnored()) {\n          i++;\n        }\n      }\n      return i;\n    }\n\n    private List<Pair<IndexedFactSet, BindingType[]>> mkNaiveIndices(\n        RelationSymbol sym, Map<BindingTypeArrayWrapper, Integer> m) {\n      List<Pair<IndexedFactSet, BindingType[]>> idxs = new ArrayList<>();\n      List<Map.Entry<BindingTypeArrayWrapper, Integer>> sorted =\n          m.entrySet().stream().sorted(cmp).collect(Collectors.toList());\n      for (Map.Entry<BindingTypeArrayWrapper, Integer> e : sorted) {\n        BindingType[] pat = e.getKey().getArr();\n        IndexedFactSet idx = IndexedFactSet.make(pat);\n        idxs.add(new Pair<>(idx, pat));\n      }\n      return idxs;\n    }\n\n    private List<Pair<IndexedFactSet, BindingType[]>> mkMinIndices(\n        RelationSymbol sym, Map<BindingTypeArrayWrapper, Integer> m) {\n      List<Pair<IndexedFactSet, BindingType[]>> indices = new ArrayList<>(m.size());\n      for (int i = 0; i < m.size(); ++i) {\n        indices.add(null);\n      }\n      for (Map.Entry<Set<Integer>, Set<Pair<Integer, BindingType[]>>> e1 :\n          partitionByIgnoredPositions(m).entrySet()) {\n        for (Map.Entry<Integer, Pair<IndexedFactSet, BindingType[]>> e2 :\n            mkMinIndices(sym, e1.getKey(), e1.getValue()).entrySet()) {\n          assert indices.get(e2.getKey()) == null;\n          indices.set(e2.getKey(), e2.getValue());\n        }\n      }\n      return indices;\n    }\n\n    private Map<Integer, Pair<IndexedFactSet, BindingType[]>> mkMinIndices(\n        RelationSymbol sym, Set<Integer> ignored, Set<Pair<Integer, BindingType[]>> s) {\n      Map<Integer, Set<Integer>> searchByNum = new HashMap<>();\n      Map<Integer, BindingType[]> bindingsByNum = new HashMap<>();\n      Set<Set<Integer>> searches = new HashSet<>();\n      for (Pair<Integer, BindingType[]> p : s) {\n        Set<Integer> search = new HashSet<>();\n        BindingType[] pat = p.snd();\n        for (int i = 0; i < pat.length; ++i) {\n          if (pat[i].isBound()) {\n            search.add(i);\n          }\n        }\n        searches.add(search);\n        int i = p.fst();\n        searchByNum.put(i, search);\n        bindingsByNum.put(i, pat);\n      }\n      Set<Integer> domain = mkDomain(sym.getArity(), ignored);\n      Map<Set<Integer>, Iterable<Integer>> indexBySearch = MinIndex.compute(domain, searches);\n      Map<Iterable<Integer>, IndexedFactSet> factSetByIndex = new HashMap<>();\n      for (Iterable<Integer> idx : indexBySearch.values()) {\n        List<Integer> order = new ArrayList<>();\n        for (int i : idx) {\n          order.add(i);\n        }\n        factSetByIndex.put(idx, IndexedFactSet.make(order));\n      }\n      Map<Integer, Pair<IndexedFactSet, BindingType[]>> indices = new HashMap<>();\n      for (int i : searchByNum.keySet()) {\n        Set<Integer> search = searchByNum.get(i);\n        BindingType[] pat = bindingsByNum.get(i);\n        Iterable<Integer> idx = indexBySearch.get(search);\n        indices.put(i, new Pair<>(factSetByIndex.get(idx), pat));\n      }\n      return indices;\n    }\n\n    private static Set<Integer> mkDomain(int arity, Set<Integer> ignored) {\n      Set<Integer> s = new HashSet<>();\n      for (int i = 0; i < arity; ++i) {\n        if (!ignored.contains(i)) {\n          s.add(i);\n        }\n      }\n      return s;\n    }\n\n    private static Map<Set<Integer>, Set<Pair<Integer, BindingType[]>>> partitionByIgnoredPositions(\n        Map<BindingTypeArrayWrapper, Integer> m) {\n      Map<Set<Integer>, Set<Pair<Integer, BindingType[]>>> byIgnoredPos = new HashMap<>();\n      for (Map.Entry<BindingTypeArrayWrapper, Integer> e : m.entrySet()) {\n        BindingType[] pat = e.getKey().getArr();\n        Set<Integer> ignored = findIgnoredPositions(pat);\n        Pair<Integer, BindingType[]> p = new Pair<>(e.getValue(), pat);\n        Util.lookupOrCreate(byIgnoredPos, ignored, () -> new HashSet<>()).add(p);\n      }\n      return byIgnoredPos;\n    }\n\n    private static Set<Integer> findIgnoredPositions(BindingType[] pat) {\n      Set<Integer> ignored = new HashSet<>();\n      for (int i = 0; i < pat.length; ++i) {\n        if (pat[i].isIgnored()) {\n          ignored.add(i);\n        }\n      }\n      return ignored;\n    }\n\n    private static final Comparator<Map.Entry<BindingTypeArrayWrapper, Integer>> cmp =\n        new Comparator<Map.Entry<BindingTypeArrayWrapper, Integer>>() {\n\n          @Override\n          public int compare(\n              Entry<BindingTypeArrayWrapper, Integer> o1,\n              Entry<BindingTypeArrayWrapper, Integer> o2) {\n            return Integer.compare(o1.getValue(), o2.getValue());\n          }\n        };\n  }\n\n  private static class IndexedFactSet {\n\n    private static final AtomicInteger idCnt = new AtomicInteger();\n    private final int id;\n    private final NavigableSet<Term[]> s;\n    private final AtomicInteger cnt = new AtomicInteger();\n    private final List<Integer> comparatorOrder;\n\n    private static final TupleComparatorGenerator gen = new TupleComparatorGenerator();\n\n    public static IndexedFactSet make(BindingType[] pat) {\n      List<Integer> order = new ArrayList<>();\n      for (int i = 0; i < pat.length; ++i) {\n        if (pat[i].isBound()) {\n          order.add(i);\n        }\n      }\n      for (int i = 0; i < pat.length; ++i) {\n        if (pat[i].isFree()) {\n          order.add(i);\n        }\n      }\n      return make(order);\n    }\n\n    private static IndexedFactSet make(List<Integer> order) {\n      int[] a = new int[order.size()];\n      for (int i = 0; i < a.length; ++i) {\n        a[i] = order.get(i);\n      }\n      Comparator<Term[]> cmp;\n      if (Configuration.genComparators) {\n        try {\n          cmp = gen.generate(a);\n        } catch (InstantiationException\n            | IllegalAccessException\n            | IllegalArgumentException\n            | InvocationTargetException\n            | NoSuchMethodException\n            | SecurityException e) {\n          throw new AssertionError(e);\n        }\n      } else {\n        cmp = new TermArrayComparator(a);\n      }\n      return new IndexedFactSet(new ConcurrentSkipListSet<>(cmp), order);\n    }\n\n    public int comparatorLength() {\n      return comparatorOrder.size();\n    }\n\n    public Iterable<Term[]> getAll() {\n      return s;\n    }\n\n    public void clear() {\n      s.clear();\n      cnt.set(0);\n    }\n\n    public boolean isEmpty() {\n      return s.isEmpty();\n    }\n\n    private IndexedFactSet(NavigableSet<Term[]> s, List<Integer> comparatorOrder) {\n      this.s = s;\n      this.comparatorOrder = comparatorOrder;\n      this.id = idCnt.getAndIncrement();\n    }\n\n    public int getId() {\n      return id;\n    }\n\n    public boolean add(Term[] arr) {\n      boolean modified = s.add(arr);\n      if (modified) {\n        cnt.incrementAndGet();\n      }\n      return modified;\n    }\n\n    public boolean addAll(Iterable<Term[]> tups) {\n      boolean modified = false;\n      int delta = 0;\n      for (Term[] tup : tups) {\n        if (s.add(tup)) {\n          modified = true;\n          delta++;\n        }\n      }\n      if (modified) {\n        cnt.addAndGet(delta);\n      }\n      return modified;\n    }\n\n    public int count() {\n      return cnt.get();\n    }\n\n    public Iterable<Term[]> lookup(Term[] tup, BindingType[] pat) {\n      Term[] lower = new Term[tup.length];\n      Term[] upper = new Term[tup.length];\n      for (int i = 0; i < tup.length; ++i) {\n        if (pat[i].isBound()) {\n          lower[i] = tup[i];\n          upper[i] = tup[i];\n        } else {\n          lower[i] = Terms.minTerm;\n          upper[i] = Terms.maxTerm;\n        }\n      }\n      return s.subSet(lower, true, upper, true);\n    }\n\n    public boolean contains(Term[] tup) {\n      return s.contains(tup);\n    }\n\n    @Override\n    public String toString() {\n      String str = \"[\\n\\t\";\n      str += \"\\t#\" + id + \" \" + comparatorOrder + \"\\n\";\n      for (Term[] tup : s) {\n        str += \"\\n\\t\";\n        str += Arrays.toString(tup);\n      }\n      return str + \"\\n]\";\n    }\n  }\n\n  private static class TermArrayComparator implements Comparator<Term[]> {\n\n    private final int[] pat;\n\n    public TermArrayComparator(int[] pat) {\n      this.pat = pat;\n    }\n\n    @Override\n    public int compare(Term[] o1, Term[] o2) {\n      for (int i = 0; i < pat.length; i++) {\n        int j = pat[i];\n        int x = o1[j].getId();\n        int y = o2[j].getId();\n        if (x < y) {\n          return -1;\n        } else if (x > y) {\n          return 1;\n        }\n      }\n      return 0;\n    }\n  }\n\n  public class IndexInfo {\n\n    private final int indexId;\n    private final List<Integer> comparatorOrder;\n    private final BindingType[] pat;\n\n    private IndexInfo(int indexId, List<Integer> comparatorOrder, BindingType[] pat) {\n      this.indexId = indexId;\n      this.comparatorOrder = Collections.unmodifiableList(comparatorOrder);\n      this.pat = pat;\n    }\n\n    public List<Integer> getComparatorOrder() {\n      return comparatorOrder;\n    }\n\n    public BindingType[] getPattern() {\n      return pat;\n    }\n\n    public int getIndexId() {\n      return indexId;\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/db/TupleComparatorGenerator.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.db;\n\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.util.IntArrayWrapper;\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport java.lang.reflect.InvocationTargetException;\nimport java.util.Comparator;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport org.apache.bcel.Const;\nimport org.apache.bcel.generic.ASTORE;\nimport org.apache.bcel.generic.ArrayType;\nimport org.apache.bcel.generic.BranchInstruction;\nimport org.apache.bcel.generic.ClassGen;\nimport org.apache.bcel.generic.ILOAD;\nimport org.apache.bcel.generic.ISTORE;\nimport org.apache.bcel.generic.InstructionConst;\nimport org.apache.bcel.generic.InstructionFactory;\nimport org.apache.bcel.generic.InstructionHandle;\nimport org.apache.bcel.generic.InstructionList;\nimport org.apache.bcel.generic.MethodGen;\nimport org.apache.bcel.generic.ObjectType;\nimport org.apache.bcel.generic.PUSH;\nimport org.apache.bcel.generic.Type;\n\npublic class TupleComparatorGenerator extends ClassLoader {\n\n  private static final Type objectType = new ObjectType(\"java.lang.Object\");\n  private static final Type termType = new ObjectType(\"edu.harvard.seas.pl.formulog.ast.Term\");\n  private static final Type termArrayType = new ArrayType(termType, 1);\n\n  private final AtomicInteger cnt = new AtomicInteger();\n  private Map<IntArrayWrapper, Comparator<Term[]>> memo = new ConcurrentHashMap<>();\n\n  public Comparator<Term[]> generate(int[] accessPat)\n      throws InstantiationException,\n          IllegalAccessException,\n          IllegalArgumentException,\n          InvocationTargetException,\n          NoSuchMethodException,\n          SecurityException {\n    IntArrayWrapper key = new IntArrayWrapper(accessPat);\n    Comparator<Term[]> cmp = memo.get(key);\n    if (cmp == null) {\n      cmp = generate1(accessPat);\n      Comparator<Term[]> cmp2 = memo.putIfAbsent(key, cmp);\n      if (cmp2 != null) {\n        cmp = cmp2;\n      }\n    }\n    return cmp;\n  }\n\n  @SuppressWarnings(\"unchecked\")\n  public Comparator<Term[]> generate1(int[] accessPat)\n      throws InstantiationException,\n          IllegalAccessException,\n          IllegalArgumentException,\n          InvocationTargetException,\n          NoSuchMethodException,\n          SecurityException {\n    String className = \"edu.harvard.seas.pl.formulog.db.CustomComparator\" + cnt.getAndIncrement();\n    ClassGen classGen =\n        new ClassGen(\n            className,\n            \"java.lang.Object\",\n            \"\",\n            Const.ACC_PUBLIC | Const.ACC_SUPER,\n            new String[] {\"java.util.Comparator\"});\n\n    classGen.addEmptyConstructor(Const.ACC_PUBLIC);\n    addCompareMethod(classGen, accessPat);\n\n    byte[] data = classGen.getJavaClass().getBytes();\n    Class<?> c = defineClass(className, data, 0, data.length);\n    return (Comparator<Term[]>) c.getDeclaredConstructor().newInstance();\n  }\n\n  private void addCompareMethod(ClassGen cg, int[] accessPat) {\n    InstructionFactory f = new InstructionFactory(cg);\n    InstructionList il = new InstructionList();\n    il.append(genCast(f, 1));\n    il.append(genCast(f, 2));\n    InstructionHandle ih = null;\n    BranchInstruction br = null;\n    for (int i : accessPat) {\n      Pair<InstructionList, BranchInstruction> p = genComparison(f, i);\n      ih = il.append(p.fst());\n      if (br != null) {\n        br.setTarget(ih);\n      }\n      br = p.snd();\n    }\n    ih = il.append(new PUSH(cg.getConstantPool(), 0));\n    if (br != null) {\n      br.setTarget(ih);\n    }\n    il.append(InstructionConst.IRETURN);\n    MethodGen mg =\n        new MethodGen(\n            Const.ACC_PUBLIC,\n            Type.INT,\n            new Type[] {objectType, objectType},\n            new String[] {\"xs\", \"ys\"},\n            \"compare\",\n            cg.getClassName(),\n            il,\n            cg.getConstantPool());\n    mg.setMaxStack();\n    mg.setMaxLocals();\n    cg.addMethod(mg.getMethod());\n  }\n\n  private InstructionList genCast(InstructionFactory f, int argn) {\n    assert argn == 1 || argn == 2;\n    InstructionList il = new InstructionList();\n    il.append(InstructionFactory.createLoad(objectType, argn));\n    il.append(f.createCast(objectType, termArrayType));\n    il.append(new ASTORE(argn));\n    return il;\n  }\n\n  private Pair<InstructionList, BranchInstruction> genComparison(InstructionFactory f, int idx) {\n    InstructionList il = new InstructionList();\n    il.append(genLoad(f, 1, idx));\n    il.append(genLoad(f, 2, idx));\n    Pair<InstructionList, BranchInstruction> p = genICmp(f, true);\n    il.append(p.fst());\n    BranchInstruction br = p.snd();\n    p = genICmp(f, false);\n    br.setTarget(il.append(p.fst()));\n    return new Pair<>(il, p.snd());\n  }\n\n  private InstructionList genLoad(InstructionFactory f, int argn, int idx) {\n    assert argn == 1 || argn == 2;\n    InstructionList il = new InstructionList();\n    il.append(argn == 1 ? InstructionConst.ALOAD_1 : InstructionConst.ALOAD_2);\n    il.append(new PUSH(f.getConstantPool(), idx));\n    il.append(InstructionConst.AALOAD);\n    il.append(\n        f.createInvoke(\n            \"edu.harvard.seas.pl.formulog.ast.Term\",\n            \"getId\",\n            Type.INT,\n            new Type[] {},\n            Const.INVOKEINTERFACE));\n    il.append(new ISTORE(argn + 2));\n    return il;\n  }\n\n  private Pair<InstructionList, BranchInstruction> genICmp(InstructionFactory f, boolean firstCmp) {\n    InstructionList il = new InstructionList();\n    il.append(new ILOAD(3));\n    il.append(new ILOAD(4));\n    BranchInstruction br =\n        InstructionFactory.createBranchInstruction(\n            firstCmp ? Const.IF_ICMPGE : Const.IF_ICMPLE, null);\n    il.append(br);\n    il.append(new PUSH(f.getConstantPool(), firstCmp ? -1 : 1));\n    il.append(InstructionConst.IRETURN);\n    return new Pair<>(il, br);\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/eval/AbstractStratumEvaluator.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2020-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.eval;\n\nimport edu.harvard.seas.pl.formulog.Configuration;\nimport edu.harvard.seas.pl.formulog.ast.BindingType;\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.ast.Var;\nimport edu.harvard.seas.pl.formulog.symbols.RelationSymbol;\nimport edu.harvard.seas.pl.formulog.unification.OverwriteSubstitution;\nimport edu.harvard.seas.pl.formulog.unification.Substitution;\nimport edu.harvard.seas.pl.formulog.util.AbstractFJPTask;\nimport edu.harvard.seas.pl.formulog.util.CountingFJP;\nimport edu.harvard.seas.pl.formulog.util.SharedLong;\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport edu.harvard.seas.pl.formulog.validating.ast.Assignment;\nimport edu.harvard.seas.pl.formulog.validating.ast.Check;\nimport edu.harvard.seas.pl.formulog.validating.ast.Destructor;\nimport edu.harvard.seas.pl.formulog.validating.ast.SimpleLiteral;\nimport edu.harvard.seas.pl.formulog.validating.ast.SimplePredicate;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * This class represents an abstract stratum evaluation method (e.g., semi-naive evaluation, eager\n * evaluation). It includes functionality common to different evaluation techniques. Most notably,\n * it splits a rule into a prefix (which conceptually contains no loops and can be processed\n * sequentially) and a suffix (which contains loops that should be parallelized); additionally, it\n * contains the code for evaluating the suffix of a rule, which is the \"hot loop\" during Datalog\n * evaluation.\n */\npublic abstract class AbstractStratumEvaluator implements StratumEvaluator {\n\n  protected final Set<IndexedRule> firstRoundRules = new HashSet<>();\n  protected final Map<RelationSymbol, Set<IndexedRule>> laterRoundRules = new HashMap<>();\n  protected final Map<IndexedRule, boolean[]> splitPositions = new HashMap<>();\n  protected final Map<IndexedRule, Integer> checkPosition = new HashMap<>();\n  protected final CountingFJP exec;\n\n  public AbstractStratumEvaluator(Iterable<IndexedRule> rules, CountingFJP exec) {\n    processRules(rules);\n    this.exec = exec;\n  }\n\n  private void processRules(Iterable<IndexedRule> rules) {\n    SmtCallFinder scf = new SmtCallFinder();\n    for (IndexedRule rule : rules) {\n      RelationSymbol delta = EvalUtil.findDelta(rule);\n      if (delta != null) {\n        Util.lookupOrCreate(laterRoundRules, delta, () -> new HashSet<>()).add(rule);\n      } else {\n        firstRoundRules.add(rule);\n      }\n      boolean[] positions = findSplitPositions(rule, scf);\n      splitPositions.put(rule, positions);\n      checkPosition.put(rule, findCheckPosition(rule));\n    }\n  }\n\n  private static boolean[] findSplitPositions(IndexedRule rule, SmtCallFinder scf) {\n    int len = rule.getBodySize();\n    boolean[] splitPositions = new boolean[len];\n    boolean smtCallComing = scf.containsSmtCall(rule.getHead());\n    for (int i = len - 1; i >= 0; --i) {\n      SimpleLiteral l = rule.getBody(i);\n      if (smtCallComing && l instanceof SimplePredicate && !((SimplePredicate) l).isNegated()) {\n        splitPositions[i] = true;\n        smtCallComing = scf.containsSmtCall(l);\n      } else {\n        smtCallComing = smtCallComing || scf.containsSmtCall(l);\n      }\n    }\n    return splitPositions;\n  }\n\n  private static int findCheckPosition(IndexedRule rule) {\n    var headVars = rule.getHead().varSet();\n    Set<Var> bound = new HashSet<>();\n    int i = 0;\n    for (; i < rule.getBodySize(); ++i) {\n      if (bound.containsAll(headVars)) {\n        break;\n      }\n      rule.getBody(i).varSet(bound);\n    }\n    return i;\n  }\n\n  protected abstract void reportFact(RelationSymbol sym, Term[] args);\n\n  protected abstract boolean checkFact(\n      RelationSymbol sym, Term[] args, Substitution s, Term[] scratch) throws EvaluationException;\n\n  protected abstract Iterable<Iterable<Term[]>> lookup(\n      IndexedRule r, int pos, OverwriteSubstitution s, boolean split) throws EvaluationException;\n\n  protected static final boolean recordRuleDiagnostics = Configuration.recordRuleDiagnostics;\n\n  @SuppressWarnings(\"serial\")\n  protected class RuleSuffixEvaluator extends AbstractFJPTask {\n\n    final IndexedRule rule;\n    final SimplePredicate head;\n    final SimpleLiteral[] body;\n    final int startPos;\n    final OverwriteSubstitution s;\n    final Iterator<Iterable<Term[]>> it;\n    final Term[] scratch;\n\n    protected RuleSuffixEvaluator(\n        IndexedRule rule,\n        SimplePredicate head,\n        SimpleLiteral[] body,\n        int pos,\n        OverwriteSubstitution s,\n        Iterator<Iterable<Term[]>> it,\n        Term[] scratch) {\n      super(exec);\n      this.rule = rule;\n      this.head = head;\n      this.body = body;\n      this.startPos = pos;\n      this.s = s;\n      this.it = it;\n      this.scratch = scratch;\n      if (Configuration.recordDetailedWork) {\n        Configuration.workItems.increment();\n      }\n    }\n\n    protected RuleSuffixEvaluator(\n        IndexedRule rule,\n        int pos,\n        OverwriteSubstitution s,\n        Iterator<Iterable<Term[]>> it,\n        Term[] scratch) {\n      super(exec);\n      this.rule = rule;\n      this.head = rule.getHead();\n      SimpleLiteral[] bd = new SimpleLiteral[rule.getBodySize()];\n      for (int i = 0; i < bd.length; ++i) {\n        bd[i] = rule.getBody(i);\n      }\n      this.body = bd;\n      this.startPos = pos;\n      this.s = s;\n      this.it = it;\n      this.scratch = scratch;\n      if (Configuration.recordDetailedWork) {\n        Configuration.workItems.increment();\n      }\n    }\n\n    @Override\n    public void doTask() throws EvaluationException {\n      long start = 0;\n      if (recordRuleDiagnostics) {\n        start = System.currentTimeMillis();\n      }\n      Iterable<Term[]> tups = it.next();\n      if (it.hasNext()) {\n        exec.recursivelyAddTask(\n            new RuleSuffixEvaluator(rule, head, body, startPos, s.copy(), it, scratch.clone()));\n      }\n      try {\n        for (Term[] tup : tups) {\n          evaluate(tup);\n        }\n      } catch (UncheckedEvaluationException e) {\n        throw new EvaluationException(\n            \"Exception raised while evaluating the rule: \" + rule + \"\\n\\n\" + e.getMessage());\n      }\n      if (recordRuleDiagnostics) {\n        long end = System.currentTimeMillis();\n        Configuration.recordRuleSuffixTime(rule, end - start);\n      }\n    }\n\n    private void evaluate(Term[] ans) throws UncheckedEvaluationException {\n      SimplePredicate p = (SimplePredicate) body[startPos];\n      updateBinding(p, ans);\n      int pos = startPos + 1;\n      @SuppressWarnings(\"unchecked\")\n      Iterator<Term[]>[] stack = new Iterator[rule.getBodySize()];\n      boolean movingRight = true;\n      var checkPos = checkPosition.get(rule);\n      while (pos > startPos) {\n        try {\n          if (movingRight\n              && checkPos == pos\n              && !checkFact(head.getSymbol(), head.getArgs(), s, scratch)) {\n            pos--;\n            movingRight = false;\n          }\n        } catch (EvaluationException e) {\n          throw new UncheckedEvaluationException(\n              \"Exception raised while evaluating the literal: \" + head + \"\\n\\n\" + e.getMessage());\n        }\n        if (pos == body.length) {\n          reportFact(head.getSymbol(), scratch);\n          pos--;\n          movingRight = false;\n        } else if (movingRight) {\n          SimpleLiteral l = body[pos];\n          try {\n            switch (l.getTag()) {\n              case ASSIGNMENT:\n                ((Assignment) l).assign(s);\n                pos++;\n                break;\n              case CHECK:\n                if (((Check) l).check(s)) {\n                  pos++;\n                } else {\n                  pos--;\n                  movingRight = false;\n                }\n                break;\n              case DESTRUCTOR:\n                if (((Destructor) l).destruct(s)) {\n                  pos++;\n                } else {\n                  pos--;\n                  movingRight = false;\n                }\n                break;\n              case PREDICATE:\n                Iterator<Iterable<Term[]>> tups =\n                    lookup(rule, pos, s, Configuration.parallelizeInnerLoops).iterator();\n                if (((SimplePredicate) l).isNegated()) {\n                  if (!tups.hasNext()) {\n                    pos++;\n                  } else {\n                    pos--;\n                    movingRight = false;\n                  }\n                } else {\n                  if (tups.hasNext()) {\n                    stack[pos] = tups.next().iterator();\n                    if (tups.hasNext()) {\n                      exec.recursivelyAddTask(\n                          new RuleSuffixEvaluator(\n                              rule, head, body, pos, s.copy(), tups, scratch.clone()));\n                    }\n                    // No need to do anything else: we'll hit the right case on the next iteration.\n                  } else {\n                    pos--;\n                  }\n                  movingRight = false;\n                }\n                break;\n            }\n          } catch (EvaluationException e) {\n            throw new UncheckedEvaluationException(\n                \"Exception raised while evaluating the literal: \" + l + \"\\n\\n\" + e.getMessage());\n          }\n        } else {\n          Iterator<Term[]> it = stack[pos];\n          if (it != null && it.hasNext()) {\n            ans = it.next();\n            updateBinding((SimplePredicate) rule.getBody(pos), ans);\n            movingRight = true;\n            pos++;\n          } else {\n            stack[pos] = null;\n            pos--;\n          }\n        }\n      }\n    }\n\n    private void updateBinding(SimplePredicate p, Term[] ans) {\n      if (Configuration.recordWork) {\n        Configuration.work.increment();\n      }\n      if (Configuration.recordDetailedWork) {\n        Util.lookupOrCreate(Configuration.workPerRule, rule, SharedLong::new).increment();\n      }\n      Term[] args = p.getArgs();\n      BindingType[] pat = p.getBindingPattern();\n      for (int i = 0; i < pat.length; ++i) {\n        if (pat[i].isFree()) {\n          s.put((Var) args[i], ans[i]);\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/eval/EagerStratumEvaluator.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2020-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.eval;\n\nimport edu.harvard.seas.pl.formulog.Configuration;\nimport edu.harvard.seas.pl.formulog.ast.BindingType;\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.ast.UserPredicate;\nimport edu.harvard.seas.pl.formulog.ast.Var;\nimport edu.harvard.seas.pl.formulog.db.SortedIndexedFactDb;\nimport edu.harvard.seas.pl.formulog.eval.SemiNaiveRule.DeltaSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.RelationSymbol;\nimport edu.harvard.seas.pl.formulog.unification.OverwriteSubstitution;\nimport edu.harvard.seas.pl.formulog.unification.Substitution;\nimport edu.harvard.seas.pl.formulog.util.AbstractFJPTask;\nimport edu.harvard.seas.pl.formulog.util.CountingFJP;\nimport edu.harvard.seas.pl.formulog.util.SharedLong;\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport edu.harvard.seas.pl.formulog.validating.ast.Assignment;\nimport edu.harvard.seas.pl.formulog.validating.ast.Check;\nimport edu.harvard.seas.pl.formulog.validating.ast.Destructor;\nimport edu.harvard.seas.pl.formulog.validating.ast.SimpleLiteral;\nimport edu.harvard.seas.pl.formulog.validating.ast.SimplePredicate;\nimport java.util.Collections;\nimport java.util.Iterator;\nimport java.util.Set;\n\n/**\n * This class implements eager evaluation of a stratum, an alternative evaluation strategy to\n * traditional semi-naive Datalog evaluation. Instead of batching newly derived tuples into explicit\n * rounds of evaluation (like semi-naive evaluation), eager evaluation eagerly pursues the\n * consequences of newly derived tuples; this is achieved by immediately submitting work items for\n * each newly derived tuple to a work-stealing thread pool that selects the next item to evaluate\n * using the LIFO order.\n */\npublic final class EagerStratumEvaluator extends AbstractStratumEvaluator {\n\n  private final SortedIndexedFactDb db;\n  private final Set<RelationSymbol> trackedRelations;\n\n  static final int taskSize = Configuration.taskSize;\n  static final int smtTaskSize = Configuration.smtTaskSize;\n\n  public EagerStratumEvaluator(\n      SortedIndexedFactDb db,\n      Iterable<IndexedRule> rules,\n      CountingFJP exec,\n      Set<RelationSymbol> trackedRelations) {\n    super(rules, exec);\n    this.db = db;\n    this.trackedRelations = trackedRelations;\n  }\n\n  @Override\n  public void evaluate() throws EvaluationException {\n    for (IndexedRule r : firstRoundRules) {\n      exec.externallyAddTask(new RulePrefixEvaluator(r, null));\n    }\n    exec.blockUntilFinished();\n    if (exec.hasFailed()) {\n      throw exec.getFailureCause();\n    }\n  }\n\n  @Override\n  protected void reportFact(RelationSymbol sym, Term[] args) {\n    Term[] copy = args.clone();\n    if (db.add(sym, copy)) {\n      Set<IndexedRule> rs = laterRoundRules.get(sym);\n      if (rs != null) {\n        for (IndexedRule r : rs) {\n          exec.recursivelyAddTask(new RulePrefixEvaluator(r, copy));\n        }\n      }\n      if (trackedRelations.contains(sym)) {\n        System.err.println(\"[TRACKED] \" + UserPredicate.make(sym, copy, false));\n      }\n      if (Configuration.recordDetailedWork) {\n        Configuration.newDerivs.increment();\n      }\n    } else if (Configuration.recordDetailedWork) {\n      Configuration.dupDerivs.increment();\n    }\n  }\n\n  @Override\n  protected boolean checkFact(RelationSymbol sym, Term[] args, Substitution s, Term[] scratch)\n      throws EvaluationException {\n    for (int i = 0; i < args.length; ++i) {\n      scratch[i] = args[i].normalize(s);\n    }\n    return !db.hasFact(sym, scratch);\n  }\n\n  @Override\n  protected Iterable<Iterable<Term[]>> lookup(\n      IndexedRule r, int pos, OverwriteSubstitution s, boolean split) throws EvaluationException {\n    SimplePredicate predicate = (SimplePredicate) r.getBody(pos);\n    int idx = r.getDbIndex(pos);\n    Term[] args = predicate.getArgs();\n    Term[] key = new Term[args.length];\n    BindingType[] pat = predicate.getBindingPattern();\n    for (int i = 0; i < args.length; ++i) {\n      if (pat[i].isBound()) {\n        key[i] = args[i].normalize(s);\n      } else {\n        key[i] = args[i];\n      }\n    }\n    RelationSymbol sym = predicate.getSymbol();\n    assert !(sym instanceof DeltaSymbol);\n    Iterable<Term[]> ans = db.get(sym, key, idx);\n    if (split) {\n      boolean smtSplit = splitPositions.get(r)[pos];\n      int targetSize = smtSplit ? smtTaskSize : taskSize;\n      return Util.splitIterable(ans, targetSize);\n    } else if (ans.iterator().hasNext()) {\n      return Collections.singletonList(ans);\n    } else {\n      return Collections.emptyList();\n    }\n  }\n\n  @SuppressWarnings(\"serial\")\n  private class RulePrefixEvaluator extends AbstractFJPTask {\n\n    private final IndexedRule rule;\n    private final Term[] deltaArgs;\n\n    protected RulePrefixEvaluator(IndexedRule rule, Term[] deltaArgs) {\n      super(exec);\n      this.rule = rule;\n      this.deltaArgs = deltaArgs;\n      if (Configuration.recordDetailedWork) {\n        Configuration.workItems.increment();\n      }\n    }\n\n    private final boolean handleDelta(SimplePredicate pred, Substitution s)\n        throws EvaluationException {\n      BindingType[] bindings = pred.getBindingPattern();\n      Term[] args = pred.getArgs();\n      int i = 0;\n      for (BindingType b : bindings) {\n        Term arg = args[i];\n        if (b.isFree()) {\n          assert arg instanceof Var;\n          s.put((Var) arg, deltaArgs[i]);\n        } else if (b.isBound()) {\n          if (!arg.normalize(s).equals(deltaArgs[i])) {\n            return false;\n          }\n        }\n        ++i;\n      }\n      if (Configuration.recordWork) {\n        Configuration.work.increment();\n      }\n      if (Configuration.recordDetailedWork) {\n        Util.lookupOrCreate(Configuration.workPerRule, rule, SharedLong::new).increment();\n      }\n      return true;\n    }\n\n    @Override\n    public final void doTask() throws EvaluationException {\n      long start = 0;\n      if (recordRuleDiagnostics) {\n        start = System.currentTimeMillis();\n      }\n      try {\n        evaluate();\n      } catch (EvaluationException e) {\n        throw new EvaluationException(\n            \"Exception raised while evaluating the rule:\\n\" + rule + \"\\n\\n\" + e.getMessage());\n      }\n      if (recordRuleDiagnostics) {\n        long end = System.currentTimeMillis();\n        Configuration.recordRulePrefixTime(rule, end - start);\n      }\n    }\n\n    private void evaluate() throws EvaluationException {\n      int len = rule.getBodySize();\n      int pos = 0;\n      OverwriteSubstitution s = new OverwriteSubstitution();\n      SimplePredicate head = rule.getHead();\n      var scratch = new Term[head.getSymbol().getArity()];\n      var checkPos = checkPosition.get(rule);\n      loop:\n      for (; pos <= len; ++pos) {\n        SimpleLiteral l = head;\n        if (checkPos == pos && !checkFact(head.getSymbol(), head.getArgs(), s, scratch)) {\n          return;\n        }\n        if (pos == len) {\n          reportFact(head.getSymbol(), scratch);\n          return;\n        }\n        l = rule.getBody(pos);\n        try {\n          switch (l.getTag()) {\n            case ASSIGNMENT:\n              ((Assignment) l).assign(s);\n              break;\n            case CHECK:\n              if (!((Check) l).check(s)) {\n                return;\n              }\n              break;\n            case DESTRUCTOR:\n              if (!((Destructor) l).destruct(s)) {\n                return;\n              }\n              break;\n            case PREDICATE:\n              SimplePredicate p = (SimplePredicate) l;\n              if (p.isNegated()) {\n                if (lookup(rule, pos, s, false).iterator().hasNext()) {\n                  return;\n                }\n              } else {\n                RelationSymbol sym = p.getSymbol();\n                if (!(sym instanceof DeltaSymbol)) {\n                  break loop;\n                }\n                if (!handleDelta(p, s)) {\n                  return;\n                }\n              }\n              break;\n          }\n        } catch (EvaluationException e) {\n          throw new EvaluationException(\n              \"Exception raised while evaluating the literal: \" + l + \"\\n\\n\" + e.getMessage());\n        }\n      }\n      Iterator<Iterable<Term[]>> tups = lookup(rule, pos, s, true).iterator();\n      if (tups.hasNext()) {\n        new RuleSuffixEvaluator(rule, pos, s, tups, scratch.clone()).doTask();\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/eval/EvalUtil.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2020-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.eval;\n\nimport edu.harvard.seas.pl.formulog.eval.SemiNaiveRule.DeltaSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.RelationSymbol;\nimport edu.harvard.seas.pl.formulog.validating.ast.Assignment;\nimport edu.harvard.seas.pl.formulog.validating.ast.Check;\nimport edu.harvard.seas.pl.formulog.validating.ast.Destructor;\nimport edu.harvard.seas.pl.formulog.validating.ast.SimpleLiteral;\nimport edu.harvard.seas.pl.formulog.validating.ast.SimpleLiteralVisitor;\nimport edu.harvard.seas.pl.formulog.validating.ast.SimplePredicate;\n\npublic final class EvalUtil {\n\n  private EvalUtil() {\n    throw new AssertionError(\"impossible\");\n  }\n\n  public static RelationSymbol findDelta(IndexedRule rule) {\n    for (SimpleLiteral l : rule) {\n      RelationSymbol delta =\n          l.accept(\n              new SimpleLiteralVisitor<Void, RelationSymbol>() {\n\n                @Override\n                public RelationSymbol visit(Assignment assignment, Void input) {\n                  return null;\n                }\n\n                @Override\n                public RelationSymbol visit(Check check, Void input) {\n                  return null;\n                }\n\n                @Override\n                public RelationSymbol visit(Destructor destructor, Void input) {\n                  return null;\n                }\n\n                @Override\n                public RelationSymbol visit(SimplePredicate predicate, Void input) {\n                  if (predicate.getSymbol() instanceof DeltaSymbol) {\n                    return ((DeltaSymbol) predicate.getSymbol()).getBaseSymbol();\n                  } else {\n                    return null;\n                  }\n                }\n              },\n              null);\n      if (delta != null) {\n        return delta;\n      }\n    }\n    return null;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/eval/Evaluation.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.eval;\n\nimport edu.harvard.seas.pl.formulog.ast.BasicRule;\nimport edu.harvard.seas.pl.formulog.ast.Program;\nimport edu.harvard.seas.pl.formulog.ast.UserPredicate;\n\npublic interface Evaluation {\n\n  void run() throws EvaluationException;\n\n  EvaluationResult getResult();\n\n  boolean hasQuery();\n\n  UserPredicate getQuery();\n\n  Program<UserPredicate, BasicRule> getInputProgram();\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/eval/EvaluationException.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.eval;\n\npublic class EvaluationException extends Exception {\n\n  private static final long serialVersionUID = 959646507426445054L;\n\n  public EvaluationException() {}\n\n  public EvaluationException(String message) {\n    super(message);\n  }\n\n  public EvaluationException(Throwable cause) {\n    super(cause);\n  }\n\n  public EvaluationException(String message, Throwable cause) {\n    super(message, cause);\n  }\n\n  public EvaluationException(\n      String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {\n    super(message, cause, enableSuppression, writableStackTrace);\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/eval/EvaluationResult.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.eval;\n\nimport edu.harvard.seas.pl.formulog.ast.UserPredicate;\nimport edu.harvard.seas.pl.formulog.symbols.RelationSymbol;\nimport java.util.Set;\n\npublic interface EvaluationResult {\n\n  Iterable<UserPredicate> getAll(RelationSymbol sym);\n\n  Iterable<UserPredicate> getQueryAnswer();\n\n  Set<RelationSymbol> getSymbols();\n\n  int getCount(RelationSymbol sym);\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/eval/IndexedRule.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.eval;\n\nimport edu.harvard.seas.pl.formulog.ast.Rule;\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport edu.harvard.seas.pl.formulog.validating.ast.Assignment;\nimport edu.harvard.seas.pl.formulog.validating.ast.Check;\nimport edu.harvard.seas.pl.formulog.validating.ast.Destructor;\nimport edu.harvard.seas.pl.formulog.validating.ast.SimpleLiteral;\nimport edu.harvard.seas.pl.formulog.validating.ast.SimpleLiteralVisitor;\nimport edu.harvard.seas.pl.formulog.validating.ast.SimplePredicate;\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.function.Function;\n\npublic class IndexedRule implements Rule<SimplePredicate, SimpleLiteral> {\n\n  private final SimplePredicate head;\n  private final List<SimpleLiteral> body;\n  private final List<Integer> idxs;\n\n  private IndexedRule(\n      Rule<SimplePredicate, SimpleLiteral> rule, Function<SimplePredicate, Integer> makeIndex) {\n    head = rule.getHead();\n    body = Util.iterableToList(rule);\n    idxs = createIndexes(makeIndex);\n  }\n\n  public static IndexedRule make(\n      Rule<SimplePredicate, SimpleLiteral> rule, Function<SimplePredicate, Integer> makeIndex) {\n    return new IndexedRule(rule, makeIndex);\n  }\n\n  private List<Integer> createIndexes(Function<SimplePredicate, Integer> makeIndex) {\n    List<Integer> idxs = new ArrayList<>();\n    for (SimpleLiteral a : body) {\n      idxs.add(\n          a.accept(\n              new SimpleLiteralVisitor<Void, Integer>() {\n\n                @Override\n                public Integer visit(Assignment assignment, Void input) {\n                  return null;\n                }\n\n                @Override\n                public Integer visit(Check check, Void input) {\n                  return null;\n                }\n\n                @Override\n                public Integer visit(Destructor destructor, Void input) {\n                  return null;\n                }\n\n                @Override\n                public Integer visit(SimplePredicate predicate, Void input) {\n                  return makeIndex.apply(predicate);\n                }\n              },\n              null));\n    }\n    return idxs;\n  }\n\n  @Override\n  public SimplePredicate getHead() {\n    return head;\n  }\n\n  @Override\n  public int getBodySize() {\n    return body.size();\n  }\n\n  @Override\n  public SimpleLiteral getBody(int idx) {\n    return body.get(idx);\n  }\n\n  public Integer getDbIndex(int idx) {\n    return idxs.get(idx);\n  }\n\n  @Override\n  public String toString() {\n    StringBuilder sb = new StringBuilder();\n    sb.append(head);\n    sb.append(\" :-\");\n    if (body.size() == 1) {\n      sb.append(\" \");\n    } else {\n      sb.append(\"\\n\\t\");\n    }\n    int i = 0;\n    for (Iterator<SimpleLiteral> it = body.iterator(); it.hasNext(); ) {\n      Integer idx = idxs.get(i);\n      if (idx != null) {\n        sb.append(idx + \": \");\n      }\n      sb.append(it.next());\n      if (it.hasNext()) {\n        sb.append(\",\\n\\t\");\n      }\n      ++i;\n    }\n    sb.append(\".\");\n    return sb.toString();\n  }\n\n  @Override\n  public Iterator<SimpleLiteral> iterator() {\n    return body.iterator();\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/eval/PredicateFunctionSetter.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.eval;\n\nimport edu.harvard.seas.pl.formulog.ast.BindingType;\nimport edu.harvard.seas.pl.formulog.ast.BoolTerm;\nimport edu.harvard.seas.pl.formulog.ast.ComplexLiteral;\nimport edu.harvard.seas.pl.formulog.ast.Constructor;\nimport edu.harvard.seas.pl.formulog.ast.Constructors;\nimport edu.harvard.seas.pl.formulog.ast.Expr;\nimport edu.harvard.seas.pl.formulog.ast.Exprs.ExprVisitor;\nimport edu.harvard.seas.pl.formulog.ast.Fold;\nimport edu.harvard.seas.pl.formulog.ast.FunctionCallFactory.FunctionCall;\nimport edu.harvard.seas.pl.formulog.ast.LetFunExpr;\nimport edu.harvard.seas.pl.formulog.ast.MatchClause;\nimport edu.harvard.seas.pl.formulog.ast.MatchExpr;\nimport edu.harvard.seas.pl.formulog.ast.Primitive;\nimport edu.harvard.seas.pl.formulog.ast.Rule;\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.ast.Terms.TermVisitor;\nimport edu.harvard.seas.pl.formulog.ast.UserPredicate;\nimport edu.harvard.seas.pl.formulog.ast.Var;\nimport edu.harvard.seas.pl.formulog.db.IndexedFactDb;\nimport edu.harvard.seas.pl.formulog.db.IndexedFactDbBuilder;\nimport edu.harvard.seas.pl.formulog.functions.DummyFunctionDef;\nimport edu.harvard.seas.pl.formulog.functions.FunctionDef;\nimport edu.harvard.seas.pl.formulog.functions.FunctionDefManager;\nimport edu.harvard.seas.pl.formulog.functions.PredicateFunctionDef;\nimport edu.harvard.seas.pl.formulog.functions.UserFunctionDef;\nimport edu.harvard.seas.pl.formulog.symbols.BuiltInConstructorSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.BuiltInFunctionSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.ConstructorSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.FunctionSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.GlobalSymbolManager;\nimport edu.harvard.seas.pl.formulog.symbols.PredicateFunctionSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.RelationSymbol;\nimport edu.harvard.seas.pl.formulog.types.BuiltInTypes;\nimport edu.harvard.seas.pl.formulog.types.FunctorType;\nimport java.util.HashSet;\nimport java.util.Set;\n\npublic class PredicateFunctionSetter {\n\n  private final FunctionDefManager defs;\n  private final IndexedFactDbBuilder<?> dbb;\n  private IndexedFactDb db;\n  Set<FunctionSymbol> visitedFunctions = new HashSet<>();\n\n  public PredicateFunctionSetter(FunctionDefManager funcs, IndexedFactDbBuilder<?> dbb) {\n    this.defs = funcs;\n    this.dbb = dbb;\n    for (FunctionSymbol sym : funcs.getFunctionSymbols()) {\n      FunctionDef def = funcs.lookup(sym);\n      if (def instanceof UserFunctionDef) {\n        preprocess(((UserFunctionDef) def).getBody());\n      }\n    }\n  }\n\n  public void setDb(IndexedFactDb db) {\n    assert this.db == null;\n    this.db = db;\n  }\n\n  public void preprocess(Rule<UserPredicate, ComplexLiteral> r) {\n    preprocess(r.getHead());\n    for (ComplexLiteral l : r) {\n      preprocess(l);\n    }\n  }\n\n  public void preprocess(ComplexLiteral l) {\n    for (Term arg : l.getArgs()) {\n      preprocess(arg);\n    }\n  }\n\n  public void preprocess(Term t) {\n    t.accept(tv, null);\n  }\n\n  private final TermVisitor<Void, Void> tv =\n      new TermVisitor<Void, Void>() {\n\n        @Override\n        public Void visit(Var t, Void in) {\n          return null;\n        }\n\n        @Override\n        public Void visit(Constructor c, Void in) {\n          for (Term arg : c.getArgs()) {\n            arg.accept(this, in);\n          }\n          return null;\n        }\n\n        @Override\n        public Void visit(Primitive<?> p, Void in) {\n          return null;\n        }\n\n        @Override\n        public Void visit(Expr e, Void in) {\n          e.accept(ev, in);\n          return null;\n        }\n      };\n\n  private final ExprVisitor<Void, Void> ev =\n      new ExprVisitor<Void, Void>() {\n\n        @Override\n        public Void visit(MatchExpr matchExpr, Void in) {\n          matchExpr.getMatchee().accept(tv, in);\n          for (MatchClause cl : matchExpr) {\n            cl.getLhs().accept(tv, in);\n            cl.getRhs().accept(tv, in);\n          }\n          return null;\n        }\n\n        @Override\n        public Void visit(FunctionCall funcCall, Void in) {\n          for (Term arg : funcCall.getArgs()) {\n            arg.accept(tv, in);\n          }\n          FunctionSymbol sym = funcCall.getSymbol();\n          if (!visitedFunctions.add(sym) || sym instanceof BuiltInFunctionSymbol) {\n            return null;\n          }\n          FunctionDef def = defs.lookup(sym);\n          if (sym instanceof PredicateFunctionSymbol) {\n            DummyFunctionDef dummy = (DummyFunctionDef) def;\n            setPredicateFunction(dummy);\n          } else if (def instanceof UserFunctionDef) {\n            ((UserFunctionDef) def).getBody().accept(tv, in);\n          }\n          return null;\n        }\n\n        @Override\n        public Void visit(LetFunExpr funcDef, Void in) {\n          throw new AssertionError(\"impossible\");\n        }\n\n        @Override\n        public Void visit(Fold fold, Void in) {\n          fold.getShamCall().accept(this, in);\n          return null;\n        }\n      };\n\n  private void setPredicateFunction(DummyFunctionDef def) {\n    if (def.getDef() != null) {\n      return;\n    }\n    PredicateFunctionSymbol sym = (PredicateFunctionSymbol) def.getSymbol();\n    BindingType[] bindings = sym.getBindings();\n    assert bindings != null;\n    BindingType[] bindingsForIndex = turnIgnoredToFree(bindings);\n    int idx = dbb.makeIndex(sym.getPredicateSymbol(), bindingsForIndex);\n    FunctorType type = sym.getCompileTimeType();\n    Term[] paddedArgs = padArgs(sym);\n    FunctionDef innerDef;\n    if (type.getRetType().equals(BuiltInTypes.bool)) {\n      innerDef = makePredicate(sym, paddedArgs, idx);\n    } else {\n      innerDef = makeAggregate(sym, paddedArgs, idx, bindingsForIndex);\n    }\n    def.setDef(innerDef);\n  }\n\n  private static BindingType[] turnIgnoredToFree(BindingType[] bindings) {\n    BindingType[] bindings2 = new BindingType[bindings.length];\n    for (int i = 0; i < bindings.length; ++i) {\n      bindings2[i] = bindings[i];\n      if (bindings2[i].isIgnored()) {\n        bindings2[i] = BindingType.FREE;\n      }\n    }\n    return bindings2;\n  }\n\n  private FunctionDef makePredicate(PredicateFunctionSymbol funcSym, Term[] paddedArgs, int idx) {\n    RelationSymbol predSym = funcSym.getPredicateSymbol();\n    return new PredicateFunctionDef() {\n\n      @Override\n      public FunctionSymbol getSymbol() {\n        return funcSym;\n      }\n\n      @Override\n      public Term evaluate(Term[] args) throws EvaluationException {\n        args = fillInPaddedArgs(funcSym, paddedArgs, args);\n        boolean b = db.get(predSym, args, idx).iterator().hasNext();\n        return BoolTerm.mk(b);\n      }\n\n      @Override\n      public int getIndex() {\n        return idx;\n      }\n\n      @Override\n      public BindingType[] getBindingsForIndex() {\n        return funcSym.getBindings();\n      }\n    };\n  }\n\n  private FunctionDef makeAggregate(\n      PredicateFunctionSymbol funcSym,\n      Term[] paddedArgs,\n      int idx,\n      BindingType[] bindingsUsedForIndex) {\n    RelationSymbol predSym = funcSym.getPredicateSymbol();\n    int arity = 0;\n    BindingType[] bindings = funcSym.getBindings();\n    for (BindingType b : bindings) {\n      if (b.isFree()) {\n        arity++;\n      }\n    }\n    final int arity2 = arity;\n    ConstructorSymbol tupSym = (arity > 1) ? GlobalSymbolManager.lookupTupleSymbol(arity) : null;\n    return new PredicateFunctionDef() {\n\n      @Override\n      public FunctionSymbol getSymbol() {\n        return funcSym;\n      }\n\n      @Override\n      public Term evaluate(Term[] args) throws EvaluationException {\n        args = fillInPaddedArgs(funcSym, paddedArgs, args);\n        Term tail = Constructors.makeZeroAry(BuiltInConstructorSymbol.NIL);\n        for (Term[] fact : db.get(predSym, args, idx)) {\n          Term[] proj = new Term[arity2];\n          int j = 0;\n          for (int i = 0; i < bindings.length; ++i) {\n            if (bindings[i].isFree()) {\n              proj[j] = fact[i];\n              ++j;\n            }\n          }\n          Term elt = tupSym == null ? proj[0] : Constructors.make(tupSym, proj);\n          tail = Constructors.make(BuiltInConstructorSymbol.CONS, new Term[] {elt, tail});\n        }\n        return tail;\n      }\n\n      @Override\n      public int getIndex() {\n        return idx;\n      }\n\n      @Override\n      public BindingType[] getBindingsForIndex() {\n        return bindingsUsedForIndex;\n      }\n    };\n  }\n\n  private Term[] padArgs(PredicateFunctionSymbol funcSym) {\n    RelationSymbol predSym = funcSym.getPredicateSymbol();\n    Term[] padded = new Term[predSym.getArity()];\n    for (int i = 0; i < padded.length; ++i) {\n      padded[i] = Var.fresh();\n    }\n    return padded;\n  }\n\n  private Term[] fillInPaddedArgs(\n      PredicateFunctionSymbol funcSym, Term[] paddedArgs, Term[] actualArgs) {\n    RelationSymbol predSym = funcSym.getPredicateSymbol();\n    Term[] newArgs = new Term[predSym.getArity()];\n    int i = 0;\n    int j = 0;\n    for (BindingType b : funcSym.getBindings()) {\n      if (b.isBound()) {\n        newArgs[i] = actualArgs[j];\n        j++;\n      } else {\n        newArgs[i] = paddedArgs[i];\n      }\n      i++;\n    }\n    return newArgs;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/eval/RoundBasedStratumEvaluator.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2020-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.eval;\n\nimport edu.harvard.seas.pl.formulog.Configuration;\nimport edu.harvard.seas.pl.formulog.ast.BindingType;\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.ast.UserPredicate;\nimport edu.harvard.seas.pl.formulog.db.SortedIndexedFactDb;\nimport edu.harvard.seas.pl.formulog.eval.SemiNaiveRule.DeltaSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.RelationSymbol;\nimport edu.harvard.seas.pl.formulog.unification.OverwriteSubstitution;\nimport edu.harvard.seas.pl.formulog.unification.Substitution;\nimport edu.harvard.seas.pl.formulog.util.AbstractFJPTask;\nimport edu.harvard.seas.pl.formulog.util.CountingFJP;\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport edu.harvard.seas.pl.formulog.validating.ast.Assignment;\nimport edu.harvard.seas.pl.formulog.validating.ast.Check;\nimport edu.harvard.seas.pl.formulog.validating.ast.Destructor;\nimport edu.harvard.seas.pl.formulog.validating.ast.SimpleLiteral;\nimport edu.harvard.seas.pl.formulog.validating.ast.SimplePredicate;\nimport java.time.LocalDateTime;\nimport java.util.Collections;\nimport java.util.Iterator;\nimport java.util.Set;\nimport org.apache.commons.lang3.time.StopWatch;\n\n/**\n * This class implements traditional semi-naive evaluation of a stratum, in which rule atoms refer\n * to auxiliary relations indexed by round of the fixpoint evaluation. For example, if the predicate\n * p is recursive in the current stratum, going into round i+1, there will be the relation\n * delta_i(p) holding the p facts derived in round i, and the relation p_{i+1} holding the entire p\n * relation at the beginning of round i+1. Unlike some versions of semi-naive evaluation, we do not\n * use the auxiliary relation p_i (that is, the relation p as it stood at the beginning of the\n * previous round) when adorning non-linearly recursive rules.\n */\npublic final class RoundBasedStratumEvaluator extends AbstractStratumEvaluator {\n\n  private final int stratumNum;\n  private final SortedIndexedFactDb db;\n  private SortedIndexedFactDb deltaDb;\n  private SortedIndexedFactDb nextDeltaDb;\n  private final Set<RelationSymbol> trackedRelations;\n  private volatile boolean changed;\n\n  private static final int taskSize = Configuration.taskSize;\n  private static final int smtTaskSize = Configuration.smtTaskSize;\n\n  public RoundBasedStratumEvaluator(\n      int stratumNum,\n      SortedIndexedFactDb db,\n      SortedIndexedFactDb deltaDb,\n      SortedIndexedFactDb nextDeltaDb,\n      Iterable<IndexedRule> rules,\n      CountingFJP exec,\n      Set<RelationSymbol> trackedRelations) {\n    super(rules, exec);\n    this.stratumNum = stratumNum;\n    this.db = db;\n    this.deltaDb = deltaDb;\n    this.nextDeltaDb = nextDeltaDb;\n    this.trackedRelations = trackedRelations;\n  }\n\n  @Override\n  public void evaluate() throws EvaluationException {\n    this.deltaDb.clear();\n    this.nextDeltaDb.clear();\n    final boolean oneRuleAtATime = Configuration.oneRuleAtATime;\n    int round = 0;\n    StopWatch watch = recordRoundStart(round);\n    for (IndexedRule r : firstRoundRules) {\n      exec.externallyAddTask(new RulePrefixEvaluator(r));\n      if (oneRuleAtATime) {\n        exec.blockUntilFinishedExn();\n      }\n    }\n    exec.blockUntilFinishedExn();\n    recordRoundEnd(round, watch);\n    updateDbs();\n    while (changed) {\n      round++;\n      watch = recordRoundStart(round);\n      changed = false;\n      for (RelationSymbol delta : laterRoundRules.keySet()) {\n        if (!deltaDb.isEmpty(delta)) {\n          for (IndexedRule r : laterRoundRules.get(delta)) {\n            exec.externallyAddTask(new RulePrefixEvaluator(r));\n            if (oneRuleAtATime) {\n              exec.blockUntilFinishedExn();\n            }\n          }\n        }\n      }\n      exec.blockUntilFinishedExn();\n      recordRoundEnd(round, watch);\n      updateDbs();\n    }\n  }\n\n  @Override\n  protected final void reportFact(RelationSymbol sym, Term[] args) {\n    var copy = args.clone();\n    if (nextDeltaDb.add(sym, copy)) {\n      changed = true;\n      if (trackedRelations.contains(sym)) {\n        System.err.println(\"[TRACKED] \" + UserPredicate.make(sym, copy, false));\n      }\n      if (Configuration.recordDetailedWork) {\n        Configuration.newDerivs.increment();\n      }\n    } else if (Configuration.recordDetailedWork) {\n      Configuration.dupDerivs.increment();\n    }\n  }\n\n  @Override\n  protected final boolean checkFact(RelationSymbol sym, Term[] args, Substitution s, Term[] scratch)\n      throws EvaluationException {\n    for (int i = 0; i < args.length; ++i) {\n      scratch[i] = args[i].normalize(s);\n    }\n    return !db.hasFact(sym, scratch) && !nextDeltaDb.hasFact(sym, scratch);\n  }\n\n  private void updateDbs() {\n    StopWatch watch = recordDbUpdateStart();\n    for (RelationSymbol sym : nextDeltaDb.getSymbols()) {\n      if (nextDeltaDb.isEmpty(sym)) {\n        continue;\n      }\n      Iterable<Iterable<Term[]>> answers = Util.splitIterable(nextDeltaDb.getAll(sym), taskSize);\n      exec.externallyAddTask(new UpdateDbTask(sym, answers.iterator()));\n    }\n    exec.blockUntilFinished();\n    SortedIndexedFactDb tmp = deltaDb;\n    deltaDb = nextDeltaDb;\n    nextDeltaDb = tmp;\n    nextDeltaDb.clear();\n    recordDbUpdateEnd(watch);\n  }\n\n  @SuppressWarnings(\"serial\")\n  private class UpdateDbTask extends AbstractFJPTask {\n\n    final RelationSymbol sym;\n    final Iterator<Iterable<Term[]>> it;\n\n    protected UpdateDbTask(RelationSymbol sym, Iterator<Iterable<Term[]>> it) {\n      super(exec);\n      this.sym = sym;\n      this.it = it;\n    }\n\n    @Override\n    public void doTask() throws EvaluationException {\n      Iterable<Term[]> tups = it.next();\n      if (it.hasNext()) {\n        exec.recursivelyAddTask(new UpdateDbTask(sym, it));\n      }\n      db.addAll(sym, tups);\n    }\n  }\n\n  @Override\n  protected final Iterable<Iterable<Term[]>> lookup(\n      IndexedRule r, int pos, OverwriteSubstitution s, boolean split) throws EvaluationException {\n    SimplePredicate predicate = (SimplePredicate) r.getBody(pos);\n    int idx = r.getDbIndex(pos);\n    Term[] args = predicate.getArgs();\n    Term[] key = new Term[args.length];\n    BindingType[] pat = predicate.getBindingPattern();\n    for (int i = 0; i < args.length; ++i) {\n      if (pat[i].isBound()) {\n        key[i] = args[i].normalize(s);\n      } else {\n        key[i] = args[i];\n      }\n    }\n    RelationSymbol sym = predicate.getSymbol();\n    Iterable<Term[]> ans;\n    if (sym instanceof DeltaSymbol) {\n      ans = deltaDb.get(((DeltaSymbol) sym).getBaseSymbol(), key, idx);\n    } else {\n      ans = db.get(sym, key, idx);\n    }\n    if (split) {\n      boolean smtSplit = splitPositions.get(r)[pos];\n      int targetSize = smtSplit ? smtTaskSize : taskSize;\n      return Util.splitIterable(ans, targetSize);\n    } else if (ans.iterator().hasNext()) {\n      return Collections.singletonList(ans);\n    } else {\n      return Collections.emptyList();\n    }\n  }\n\n  @SuppressWarnings(\"serial\")\n  private class RulePrefixEvaluator extends AbstractFJPTask {\n\n    private final IndexedRule rule;\n\n    protected RulePrefixEvaluator(IndexedRule rule) {\n      super(exec);\n      this.rule = rule;\n      if (Configuration.recordDetailedWork) {\n        Configuration.workItems.increment();\n      }\n    }\n\n    @Override\n    public void doTask() throws EvaluationException {\n      long start = 0;\n      if (recordRuleDiagnostics) {\n        start = System.currentTimeMillis();\n      }\n      try {\n        evaluate();\n      } catch (EvaluationException e) {\n        throw new EvaluationException(\n            \"Exception raised while evaluating the rule:\\n\" + rule + \"\\n\\n\" + e.getMessage());\n      }\n      if (recordRuleDiagnostics) {\n        long end = System.currentTimeMillis();\n        Configuration.recordRulePrefixTime(rule, end - start);\n      }\n    }\n\n    private void evaluate() throws EvaluationException {\n      int len = rule.getBodySize();\n      int pos = 0;\n      OverwriteSubstitution s = new OverwriteSubstitution();\n      SimplePredicate head = rule.getHead();\n      Term[] scratch = new Term[head.getSymbol().getArity()];\n      int checkPos = checkPosition.get(rule);\n      loop:\n      for (; pos <= len; ++pos) {\n        SimpleLiteral l = head;\n        if (checkPos == pos && !checkFact(head.getSymbol(), head.getArgs(), s, scratch)) {\n          return;\n        }\n        if (pos == len) {\n          reportFact(head.getSymbol(), scratch);\n          return;\n        }\n        l = rule.getBody(pos);\n        try {\n          switch (l.getTag()) {\n            case ASSIGNMENT:\n              ((Assignment) l).assign(s);\n              break;\n            case CHECK:\n              if (!((Check) l).check(s)) {\n                return;\n              }\n              break;\n            case DESTRUCTOR:\n              if (!((Destructor) l).destruct(s)) {\n                return;\n              }\n              break;\n            case PREDICATE:\n              SimplePredicate p = (SimplePredicate) l;\n              if (p.isNegated()) {\n                if (lookup(rule, pos, s, false).iterator().hasNext()) {\n                  return;\n                }\n              } else {\n                // Stop on the first positive user predicate.\n                break loop;\n              }\n              break;\n          }\n        } catch (EvaluationException e) {\n          throw new EvaluationException(\n              \"Exception raised while evaluating the literal: \" + l + \"\\n\\n\" + e.getMessage());\n        }\n      }\n      Iterator<Iterable<Term[]>> tups = lookup(rule, pos, s, true).iterator();\n      if (tups.hasNext()) {\n        new RuleSuffixEvaluator(rule, pos, s, tups, scratch.clone()).doTask();\n      }\n    }\n  }\n\n  private StopWatch recordRoundStart(int round) {\n    if (!Configuration.debugRounds) {\n      return null;\n    }\n    System.err.println(\"#####\");\n    System.err.println(\"[START] Stratum \" + stratumNum + \", round \" + round);\n    LocalDateTime now = LocalDateTime.now();\n    System.err.println(\"Start: \" + now);\n    StopWatch watch = new StopWatch();\n    watch.start();\n    return watch;\n  }\n\n  private void recordRoundEnd(int round, StopWatch watch) {\n    if (watch == null) {\n      return;\n    }\n    watch.stop();\n    System.err.println(\"[END] Stratum \" + stratumNum + \", round \" + round);\n    System.err.println(\"Time: \" + watch.getTime() + \"ms\");\n  }\n\n  private StopWatch recordDbUpdateStart() {\n    if (!Configuration.debugRounds) {\n      return null;\n    }\n    System.err.println(\"[START] Updating DBs\");\n    LocalDateTime now = LocalDateTime.now();\n    System.err.println(\"Start: \" + now);\n    StopWatch watch = new StopWatch();\n    watch.start();\n    return watch;\n  }\n\n  private void recordDbUpdateEnd(StopWatch watch) {\n    if (watch == null) {\n      return;\n    }\n    watch.stop();\n    Configuration.printRelSizes(System.err, \"DELTA SIZE\", deltaDb, false);\n    System.err.println(\"[END] Updating DBs\");\n    System.err.println(\"Time: \" + watch.getTime() + \"ms\");\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/eval/SemiNaiveEvaluation.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.eval;\n\nimport edu.harvard.seas.pl.formulog.Configuration;\nimport edu.harvard.seas.pl.formulog.Main;\nimport edu.harvard.seas.pl.formulog.ast.BasicProgram;\nimport edu.harvard.seas.pl.formulog.ast.BasicRule;\nimport edu.harvard.seas.pl.formulog.ast.ComplexLiteral;\nimport edu.harvard.seas.pl.formulog.ast.ComplexLiterals.ComplexLiteralVisitor;\nimport edu.harvard.seas.pl.formulog.ast.Rule;\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.ast.Terms;\nimport edu.harvard.seas.pl.formulog.ast.UnificationPredicate;\nimport edu.harvard.seas.pl.formulog.ast.UserPredicate;\nimport edu.harvard.seas.pl.formulog.ast.Var;\nimport edu.harvard.seas.pl.formulog.db.IndexedFactDbBuilder;\nimport edu.harvard.seas.pl.formulog.db.SortedIndexedFactDb;\nimport edu.harvard.seas.pl.formulog.db.SortedIndexedFactDb.SortedIndexedFactDbBuilder;\nimport edu.harvard.seas.pl.formulog.eval.SemiNaiveRule.DeltaSymbol;\nimport edu.harvard.seas.pl.formulog.functions.FunctionDefManager;\nimport edu.harvard.seas.pl.formulog.magic.MagicSetTransformer;\nimport edu.harvard.seas.pl.formulog.smt.BestMatchSmtManager;\nimport edu.harvard.seas.pl.formulog.smt.CallAndResetSolver;\nimport edu.harvard.seas.pl.formulog.smt.CheckSatAssumingSolver;\nimport edu.harvard.seas.pl.formulog.smt.DoubleCheckingSolver;\nimport edu.harvard.seas.pl.formulog.smt.NotThreadSafeQueueSmtManager;\nimport edu.harvard.seas.pl.formulog.smt.PerThreadSmtManager;\nimport edu.harvard.seas.pl.formulog.smt.PushPopNaiveSolver;\nimport edu.harvard.seas.pl.formulog.smt.PushPopSolver;\nimport edu.harvard.seas.pl.formulog.smt.QueueSmtManager;\nimport edu.harvard.seas.pl.formulog.smt.SingleShotSolver;\nimport edu.harvard.seas.pl.formulog.smt.SmtLibSolver;\nimport edu.harvard.seas.pl.formulog.smt.SmtStrategy;\nimport edu.harvard.seas.pl.formulog.symbols.RelationSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.Symbol;\nimport edu.harvard.seas.pl.formulog.symbols.SymbolManager;\nimport edu.harvard.seas.pl.formulog.types.WellTypedProgram;\nimport edu.harvard.seas.pl.formulog.unification.SimpleSubstitution;\nimport edu.harvard.seas.pl.formulog.util.AbstractFJPTask;\nimport edu.harvard.seas.pl.formulog.util.CountingFJP;\nimport edu.harvard.seas.pl.formulog.util.CountingFJPImpl;\nimport edu.harvard.seas.pl.formulog.util.MockCountingFJP;\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport edu.harvard.seas.pl.formulog.validating.FunctionDefValidation;\nimport edu.harvard.seas.pl.formulog.validating.InvalidProgramException;\nimport edu.harvard.seas.pl.formulog.validating.Stratifier;\nimport edu.harvard.seas.pl.formulog.validating.Stratum;\nimport edu.harvard.seas.pl.formulog.validating.ValidRule;\nimport edu.harvard.seas.pl.formulog.validating.ast.SimpleRule;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.BiFunction;\n\npublic class SemiNaiveEvaluation implements Evaluation {\n\n  private final SortedIndexedFactDb db;\n  private final SortedIndexedFactDb deltaDb;\n  private final SortedIndexedFactDb nextDeltaDb;\n  private final List<Stratum> strata;\n  private final UserPredicate query;\n  private final CountingFJP exec;\n  private final Set<RelationSymbol> trackedRelations;\n  private final WellTypedProgram inputProgram;\n  private final Map<RelationSymbol, Set<IndexedRule>> rules;\n  private final boolean eagerEval;\n\n  static final boolean sequential = System.getProperty(\"sequential\") != null;\n  static final boolean debugRounds = Configuration.debugRounds;\n\n  @SuppressWarnings(\"serial\")\n  public static SemiNaiveEvaluation setup(WellTypedProgram prog, int parallelism, boolean eagerEval)\n      throws InvalidProgramException {\n    FunctionDefValidation.validate(prog);\n    MagicSetTransformer mst = new MagicSetTransformer(prog);\n    BasicProgram magicProg =\n        mst.transform(Configuration.useDemandTransformation, Configuration.restoreStratification);\n    Set<RelationSymbol> allRelations = new HashSet<>(magicProg.getFactSymbols());\n    allRelations.addAll(magicProg.getRuleSymbols());\n    allRelations.addAll(prog.getRuleSymbols());\n    SortedIndexedFactDbBuilder dbb = new SortedIndexedFactDbBuilder(allRelations);\n    SortedIndexedFactDbBuilder deltaDbb =\n        new SortedIndexedFactDbBuilder(magicProg.getRuleSymbols());\n    PredicateFunctionSetter predFuncs =\n        new PredicateFunctionSetter(magicProg.getFunctionCallFactory().getDefManager(), dbb);\n\n    Map<RelationSymbol, Set<IndexedRule>> rules = new HashMap<>();\n    List<Stratum> strata = new Stratifier(magicProg).stratify();\n    for (Stratum stratum : strata) {\n      if (stratum.hasRecursiveNegationOrAggregation()) {\n        throw new InvalidProgramException(\n            \"Cannot handle recursive negation or aggregation: \" + stratum);\n      }\n      Set<RelationSymbol> stratumSymbols = stratum.getPredicateSyms();\n      for (RelationSymbol sym : stratumSymbols) {\n        Set<IndexedRule> rs = new HashSet<>();\n        for (BasicRule br : magicProg.getRules(sym)) {\n          for (SemiNaiveRule snr : SemiNaiveRule.make(br, stratumSymbols)) {\n            BiFunction<ComplexLiteral, Set<Var>, Integer> score = chooseScoringFunction(eagerEval);\n            ValidRule vr = ValidRule.make(tweakDeltaAtom(snr), score);\n            checkRule(vr, eagerEval);\n            predFuncs.preprocess(vr);\n            SimpleRule sr = SimpleRule.make(vr, magicProg.getFunctionCallFactory());\n            IndexedRule ir =\n                IndexedRule.make(\n                    sr,\n                    p -> {\n                      RelationSymbol psym = p.getSymbol();\n                      if (psym instanceof DeltaSymbol) {\n                        psym = ((DeltaSymbol) psym).getBaseSymbol();\n                        return deltaDbb.makeIndex(psym, p.getBindingPattern());\n                      } else {\n                        return dbb.makeIndex(psym, p.getBindingPattern());\n                      }\n                    });\n            rs.add(ir);\n            if (Configuration.printFinalRules) {\n              System.err.println(\"[FINAL RULE]:\\n\" + ir);\n            }\n          }\n        }\n        rules.put(sym, rs);\n      }\n    }\n    SortedIndexedFactDb db = dbb.build();\n    predFuncs.setDb(db);\n\n    SmtLibSolver smt = getSmtManager();\n    try {\n      smt.start(magicProg);\n    } catch (EvaluationException e) {\n      throw new InvalidProgramException(\"Problem initializing SMT shims: \" + e.getMessage());\n    }\n    FunctionDefManager defManager = magicProg.getFunctionCallFactory().getDefManager();\n    defManager.loadBuiltInFunctions(smt);\n\n    CountingFJP exec;\n    if (sequential) {\n      exec = new MockCountingFJP();\n    } else {\n      exec = new CountingFJPImpl(parallelism);\n    }\n\n    for (RelationSymbol sym : magicProg.getFactSymbols()) {\n      for (Iterable<Term[]> tups :\n          Util.splitIterable(magicProg.getFacts(sym), Configuration.taskSize)) {\n        exec.externallyAddTask(\n            new AbstractFJPTask(exec) {\n\n              @Override\n              public void doTask() throws EvaluationException {\n                for (Term[] tup : tups) {\n                  try {\n                    db.add(sym, Terms.normalize(tup, new SimpleSubstitution()));\n                  } catch (EvaluationException e) {\n                    UserPredicate p = UserPredicate.make(sym, tup, false);\n                    throw new EvaluationException(\n                        \"Cannot normalize fact \" + p + \":\\n\" + e.getMessage());\n                  }\n                }\n              }\n            });\n      }\n    }\n    exec.blockUntilFinished();\n    if (exec.hasFailed()) {\n      exec.shutdown();\n      throw new InvalidProgramException(exec.getFailureCause());\n    }\n    return new SemiNaiveEvaluation(\n        prog,\n        db,\n        deltaDbb,\n        rules,\n        magicProg.getQuery(),\n        strata,\n        exec,\n        getTrackedRelations(magicProg.getSymbolManager()),\n        eagerEval);\n  }\n\n  private static Rule<UserPredicate, ComplexLiteral> tweakDeltaAtom(\n      Rule<UserPredicate, ComplexLiteral> r) {\n    List<ComplexLiteral> newBody = new ArrayList<>();\n    for (ComplexLiteral l : r) {\n      l.accept(\n          new ComplexLiteralVisitor<Void, Void>() {\n\n            @Override\n            public Void visit(UnificationPredicate unificationPredicate, Void input) {\n              newBody.add(unificationPredicate);\n              return null;\n            }\n\n            @Override\n            public Void visit(UserPredicate userPredicate, Void input) {\n              RelationSymbol sym = userPredicate.getSymbol();\n              if (sym instanceof DeltaSymbol) {\n                Term[] args = userPredicate.getArgs();\n                Term[] newArgs = new Term[args.length];\n                for (int i = 0; i < args.length; ++i) {\n                  Term arg = args[i];\n                  if (arg.containsUnevaluatedTerm()) {\n                    Var x = Var.fresh();\n                    newBody.add(UnificationPredicate.make(x, arg, false));\n                    arg = x;\n                  }\n                  newArgs[i] = arg;\n                }\n                newBody.add(UserPredicate.make(sym, newArgs, false));\n              } else {\n                newBody.add(userPredicate);\n              }\n              return null;\n            }\n          },\n          null);\n    }\n    return BasicRule.make(r.getHead(), newBody);\n  }\n\n  private static void checkRule(ValidRule r, boolean eagerEval) throws InvalidProgramException {\n    if (!eagerEval) {\n      return;\n    }\n    boolean seenUserPred = false;\n    for (ComplexLiteral l : r) {\n      if (l instanceof UserPredicate) {\n        UserPredicate pred = (UserPredicate) l;\n        if (seenUserPred && pred.getSymbol() instanceof DeltaSymbol) {\n          throw new InvalidProgramException(\"Delta symbol could not be placed first:\\n\" + r);\n        }\n        seenUserPred = true;\n      }\n    }\n  }\n\n  private static SmtLibSolver maybeDoubleCheckSolver(SmtLibSolver inner) {\n    if (Configuration.smtDoubleCheckUnknowns) {\n      return new DoubleCheckingSolver(inner);\n    }\n    return inner;\n  }\n\n  private static SmtLibSolver makeNaiveSolver() {\n    return Configuration.smtUseSingleShotSolver ? new SingleShotSolver() : new CallAndResetSolver();\n  }\n\n  private static SmtLibSolver getSmtManager() {\n    SmtStrategy strategy = Main.smtStrategy;\n    switch (strategy.getTag()) {\n      case QUEUE:\n        {\n          int size = (int) strategy.getMetadata();\n          return new QueueSmtManager(\n              size, () -> maybeDoubleCheckSolver(new CheckSatAssumingSolver()));\n        }\n      case NAIVE:\n        return maybeDoubleCheckSolver(makeNaiveSolver());\n      case PUSH_POP:\n        return new PushPopSolver();\n      case PUSH_POP_NAIVE:\n        return new PushPopNaiveSolver();\n      case BEST_MATCH:\n        {\n          int size = (int) strategy.getMetadata();\n          return maybeDoubleCheckSolver(new BestMatchSmtManager(size));\n        }\n      case PER_THREAD_QUEUE:\n        {\n          int size = (int) strategy.getMetadata();\n          return new PerThreadSmtManager(\n              () ->\n                  new NotThreadSafeQueueSmtManager(\n                      size, () -> maybeDoubleCheckSolver(new CheckSatAssumingSolver())));\n        }\n      case PER_THREAD_BEST_MATCH:\n        {\n          int size = (int) strategy.getMetadata();\n          return new PerThreadSmtManager(\n              () -> maybeDoubleCheckSolver(new BestMatchSmtManager(size)));\n        }\n      case PER_THREAD_PUSH_POP:\n        {\n          return new PerThreadSmtManager(() -> new PushPopSolver());\n        }\n      case PER_THREAD_PUSH_POP_NAIVE:\n        {\n          return new PerThreadSmtManager(() -> new PushPopNaiveSolver());\n        }\n      case PER_THREAD_NAIVE:\n        {\n          return new PerThreadSmtManager(\n              () ->\n                  maybeDoubleCheckSolver(\n                      Configuration.smtUseSingleShotSolver\n                          ? new SingleShotSolver()\n                          : new CallAndResetSolver()));\n        }\n      default:\n        throw new UnsupportedOperationException(\"Cannot support SMT strategy: \" + strategy);\n    }\n  }\n\n  static Set<RelationSymbol> getTrackedRelations(SymbolManager sm) {\n    Set<RelationSymbol> s = new HashSet<>();\n    for (String name : Configuration.trackedRelations) {\n      if (sm.hasName(name)) {\n        Symbol sym = sm.lookupSymbol(name);\n        if (sym instanceof RelationSymbol) {\n          s.add((RelationSymbol) sm.lookupSymbol(name));\n        } else {\n          System.err.println(\"[WARNING] Cannot track non-relation \" + sym);\n        }\n\n      } else {\n        System.err.println(\"[WARNING] Cannot track unrecognized relation \" + name);\n      }\n    }\n    return s;\n  }\n\n  static BiFunction<ComplexLiteral, Set<Var>, Integer> chooseScoringFunction(boolean eagerEval) {\n    if (eagerEval) {\n      return SemiNaiveEvaluation::score5;\n    }\n    switch (Configuration.optimizationSetting) {\n      case 0:\n        return SemiNaiveEvaluation::score0;\n      case 1:\n        return SemiNaiveEvaluation::score1;\n      case 2:\n        return SemiNaiveEvaluation::score2;\n      case 3:\n        return SemiNaiveEvaluation::score3;\n      case 4:\n        return SemiNaiveEvaluation::score4;\n      case 5:\n        return SemiNaiveEvaluation::score5;\n      default:\n        throw new IllegalArgumentException(\n            \"Unrecognized optimization setting: \" + Configuration.optimizationSetting);\n    }\n  }\n\n  static int score0(ComplexLiteral l, Set<Var> boundVars) {\n    return 0;\n  }\n\n  static int score1(ComplexLiteral l, Set<Var> boundVars) {\n    // This seems to be worse than just doing nothing.\n    return l.accept(\n        new ComplexLiteralVisitor<Void, Integer>() {\n\n          @Override\n          public Integer visit(UnificationPredicate unificationPredicate, Void input) {\n            return Integer.MAX_VALUE;\n          }\n\n          @Override\n          public Integer visit(UserPredicate pred, Void input) {\n            if (pred.isNegated()) {\n              return Integer.MAX_VALUE;\n            }\n            if (pred.getSymbol() instanceof DeltaSymbol) {\n              return 100;\n            }\n            Term[] args = pred.getArgs();\n            if (args.length == 0) {\n              return 150;\n            }\n            int bound = 0;\n            for (int i = 0; i < args.length; ++i) {\n              if (boundVars.containsAll(args[i].varSet())) {\n                bound++;\n              }\n            }\n            double percentBound = ((double) bound) / args.length * 100;\n            return (int) percentBound;\n          }\n        },\n        null);\n  }\n\n  static int score2(ComplexLiteral l, Set<Var> boundVars) {\n    return l.accept(\n        new ComplexLiteralVisitor<Void, Integer>() {\n\n          @Override\n          public Integer visit(UnificationPredicate unificationPredicate, Void input) {\n            return Integer.MAX_VALUE;\n          }\n\n          @Override\n          public Integer visit(UserPredicate pred, Void input) {\n            Term[] args = pred.getArgs();\n            if (args.length == 0) {\n              return 150;\n            }\n            int bound = 0;\n            for (int i = 0; i < args.length; ++i) {\n              if (boundVars.containsAll(args[i].varSet())) {\n                bound++;\n              }\n            }\n            double percentBound = ((double) bound) / args.length * 100;\n            return (int) percentBound;\n          }\n        },\n        null);\n  }\n\n  static int score3(ComplexLiteral l, Set<Var> boundVars) {\n    return l.accept(\n        new ComplexLiteralVisitor<Void, Integer>() {\n\n          @Override\n          public Integer visit(UnificationPredicate unificationPredicate, Void input) {\n            return Integer.MAX_VALUE;\n          }\n\n          @Override\n          public Integer visit(UserPredicate pred, Void input) {\n            if (pred.isNegated()) {\n              return Integer.MAX_VALUE;\n            }\n            if (pred.getSymbol() instanceof DeltaSymbol) {\n              return 125;\n            }\n            Term[] args = pred.getArgs();\n            if (args.length == 0) {\n              return Integer.MAX_VALUE;\n            }\n            int bound = 0;\n            for (int i = 0; i < args.length; ++i) {\n              if (boundVars.containsAll(args[i].varSet())) {\n                bound++;\n              }\n            }\n            double percentBound = ((double) bound) / args.length * 100;\n            return (int) percentBound;\n          }\n        },\n        null);\n  }\n\n  static int score4(ComplexLiteral l, Set<Var> boundVars) {\n    return l.accept(\n        new ComplexLiteralVisitor<Void, Integer>() {\n\n          @Override\n          public Integer visit(UnificationPredicate unificationPredicate, Void input) {\n            return Integer.MAX_VALUE;\n          }\n\n          @Override\n          public Integer visit(UserPredicate pred, Void input) {\n            if (pred.isNegated() || pred.getSymbol() instanceof DeltaSymbol) {\n              return Integer.MAX_VALUE;\n            }\n            return 0;\n          }\n        },\n        null);\n  }\n\n  static int score5(ComplexLiteral l, Set<Var> boundVars) {\n    return l.accept(\n        new ComplexLiteralVisitor<Void, Integer>() {\n\n          @Override\n          public Integer visit(UnificationPredicate unificationPredicate, Void input) {\n            return 0;\n          }\n\n          @Override\n          public Integer visit(UserPredicate pred, Void input) {\n            if (pred.getSymbol() instanceof DeltaSymbol) {\n              return Integer.MAX_VALUE;\n            }\n            return 0;\n          }\n        },\n        null);\n  }\n\n  SemiNaiveEvaluation(\n      WellTypedProgram inputProgram,\n      SortedIndexedFactDb db,\n      IndexedFactDbBuilder<SortedIndexedFactDb> deltaDbb,\n      Map<RelationSymbol, Set<IndexedRule>> rules,\n      UserPredicate query,\n      List<Stratum> strata,\n      CountingFJP exec,\n      Set<RelationSymbol> trackedRelations,\n      boolean eagerEval) {\n    this.inputProgram = inputProgram;\n    this.db = db;\n    this.query = query;\n    this.strata = strata;\n    this.exec = exec;\n    this.trackedRelations = trackedRelations;\n    this.deltaDb = deltaDbb.build();\n    this.nextDeltaDb = deltaDbb.build();\n    this.rules = rules;\n    this.eagerEval = eagerEval;\n  }\n\n  @Override\n  public WellTypedProgram getInputProgram() {\n    return inputProgram;\n  }\n\n  @Override\n  public synchronized void run() throws EvaluationException {\n    if (Configuration.printRelSizes) {\n      Runtime.getRuntime()\n          .addShutdownHook(\n              new Thread() {\n\n                @Override\n                public void run() {\n                  Configuration.printRelSizes(System.err, \"REL SIZE\", db, true);\n                }\n              });\n    }\n    if (Configuration.debugParallelism) {\n      Runtime.getRuntime()\n          .addShutdownHook(\n              new Thread() {\n\n                @Override\n                public void run() {\n                  System.err.println(\"[STEAL COUNT] \" + exec.getStealCount());\n                }\n              });\n    }\n    if (Configuration.recordWork) {\n      Runtime.getRuntime()\n          .addShutdownHook(\n              new Thread() {\n                @Override\n                public void run() {\n                  System.err.println(\"[WORK] \" + Configuration.work.unsafeGet());\n                }\n              });\n    }\n    if (Configuration.recordDetailedWork) {\n      Runtime.getRuntime()\n          .addShutdownHook(\n              new Thread() {\n                @Override\n                public void run() {\n                  System.err.println(\"[WORK ITEMS] \" + Configuration.workItems.unsafeGet());\n                  System.err.println(\"[NEW DERIVS] \" + Configuration.newDerivs.unsafeGet());\n                  System.err.println(\"[DUP DERIVS] \" + Configuration.dupDerivs.unsafeGet());\n                  Configuration.workPerRule.entrySet().stream()\n                      .map(e -> new Pair<>(e.getKey(), e.getValue().unsafeGet()))\n                      .sorted((p1, p2) -> Long.compare(p2.snd(), p1.snd()))\n                      .forEach(\n                          p -> {\n                            System.err.println(\"[PER RULE WORK] \" + p.snd() + \" \" + p.fst());\n                          });\n                }\n              });\n    }\n    for (Stratum stratum : strata) {\n      evaluateStratum(stratum);\n    }\n  }\n\n  private void evaluateStratum(Stratum stratum) throws EvaluationException {\n    List<IndexedRule> l = new ArrayList<>();\n    for (RelationSymbol sym : stratum.getPredicateSyms()) {\n      l.addAll(rules.get(sym));\n    }\n    if (eagerEval) {\n      new EagerStratumEvaluator(db, l, exec, trackedRelations).evaluate();\n    } else {\n      new RoundBasedStratumEvaluator(\n              stratum.getRank(), db, deltaDb, nextDeltaDb, l, exec, trackedRelations)\n          .evaluate();\n    }\n  }\n\n  @Override\n  public synchronized EvaluationResult getResult() {\n    return new EvaluationResult() {\n\n      @Override\n      public Iterable<UserPredicate> getAll(RelationSymbol sym) {\n        if (!db.getSymbols().contains(sym)) {\n          throw new IllegalArgumentException(\"Unrecognized relation symbol \" + sym);\n        }\n        return () -> new FactIterator(sym, db.getAll(sym).iterator());\n      }\n\n      @Override\n      public Iterable<UserPredicate> getQueryAnswer() {\n        if (query == null) {\n          return null;\n        }\n        RelationSymbol querySym = query.getSymbol();\n        return () -> new FactIterator(querySym, db.getAll(querySym).iterator());\n      }\n\n      @Override\n      public Set<RelationSymbol> getSymbols() {\n        return Collections.unmodifiableSet(db.getSymbols());\n      }\n\n      @Override\n      public int getCount(RelationSymbol sym) {\n        return db.countDistinct(sym);\n      }\n    };\n  }\n\n  static class FactIterator implements Iterator<UserPredicate> {\n\n    final RelationSymbol sym;\n    final Iterator<Term[]> bindings;\n\n    public FactIterator(RelationSymbol sym, Iterator<Term[]> bindings) {\n      this.sym = sym;\n      this.bindings = bindings;\n    }\n\n    @Override\n    public boolean hasNext() {\n      return bindings.hasNext();\n    }\n\n    @Override\n    public UserPredicate next() {\n      return UserPredicate.make(sym, bindings.next(), false);\n    }\n  }\n\n  @Override\n  public boolean hasQuery() {\n    return query != null;\n  }\n\n  @Override\n  public UserPredicate getQuery() {\n    return query;\n  }\n\n  public SortedIndexedFactDb getDb() {\n    return db;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/eval/SemiNaiveRule.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.eval;\n\nimport edu.harvard.seas.pl.formulog.ast.AbstractRule;\nimport edu.harvard.seas.pl.formulog.ast.ComplexLiteral;\nimport edu.harvard.seas.pl.formulog.ast.ComplexLiterals.ComplexLiteralVisitor;\nimport edu.harvard.seas.pl.formulog.ast.Rule;\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.ast.UnificationPredicate;\nimport edu.harvard.seas.pl.formulog.ast.UserPredicate;\nimport edu.harvard.seas.pl.formulog.symbols.AbstractWrappedRelationSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.RelationSymbol;\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\npublic class SemiNaiveRule extends AbstractRule<UserPredicate, ComplexLiteral> {\n\n  private SemiNaiveRule(UserPredicate head, List<ComplexLiteral> body) {\n    super(head, body);\n  }\n\n  public static Set<SemiNaiveRule> make(\n      Rule<UserPredicate, ComplexLiteral> rule, Set<RelationSymbol> stratumSymbols) {\n    Set<SemiNaiveRule> rules = new HashSet<>();\n    for (int i = 0; i < rule.getBodySize(); ++i) {\n      boolean canBeDelta =\n          rule.getBody(i)\n              .accept(\n                  new ComplexLiteralVisitor<Void, Boolean>() {\n\n                    @Override\n                    public Boolean visit(UnificationPredicate unificationPredicate, Void input) {\n                      return false;\n                    }\n\n                    @Override\n                    public Boolean visit(UserPredicate userPredicate, Void input) {\n                      return stratumSymbols.contains(userPredicate.getSymbol());\n                    }\n                  },\n                  null);\n      if (canBeDelta) {\n        rules.add(make(rule, stratumSymbols, i));\n      }\n    }\n    if (rules.isEmpty()) {\n      rules.add(new SemiNaiveRule(rule.getHead(), Util.iterableToList(rule)));\n    }\n    return rules;\n  }\n\n  private static SemiNaiveRule make(\n      Rule<UserPredicate, ComplexLiteral> rule, Set<RelationSymbol> stratumSymbols, int deltaIdx) {\n    List<ComplexLiteral> body = new ArrayList<>();\n    for (int i = 0; i < rule.getBodySize(); ++i) {\n      ComplexLiteral l = rule.getBody(i);\n      if (i == deltaIdx) {\n        UserPredicate p = (UserPredicate) l;\n        RelationSymbol sym = p.getSymbol();\n        Term[] args = p.getArgs();\n        l = UserPredicate.make(new DeltaSymbol(sym), args, p.isNegated());\n      }\n      body.add(l);\n    }\n    return new SemiNaiveRule(rule.getHead(), body);\n  }\n\n  public static class DeltaSymbol extends AbstractWrappedRelationSymbol<RelationSymbol> {\n\n    public DeltaSymbol(RelationSymbol baseSymbol) {\n      super(baseSymbol);\n      assert baseSymbol.isIdbSymbol();\n    }\n\n    @Override\n    public String toString() {\n      return \"delta:\" + getBaseSymbol();\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/eval/SmtCallFinder.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.eval;\n\nimport edu.harvard.seas.pl.formulog.ast.Constructor;\nimport edu.harvard.seas.pl.formulog.ast.Expr;\nimport edu.harvard.seas.pl.formulog.ast.Exprs.ExprVisitor;\nimport edu.harvard.seas.pl.formulog.ast.Fold;\nimport edu.harvard.seas.pl.formulog.ast.FunctionCallFactory.FunctionCall;\nimport edu.harvard.seas.pl.formulog.ast.LetFunExpr;\nimport edu.harvard.seas.pl.formulog.ast.Literal;\nimport edu.harvard.seas.pl.formulog.ast.MatchClause;\nimport edu.harvard.seas.pl.formulog.ast.MatchExpr;\nimport edu.harvard.seas.pl.formulog.ast.Primitive;\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.ast.Terms.TermVisitor;\nimport edu.harvard.seas.pl.formulog.ast.Var;\nimport edu.harvard.seas.pl.formulog.functions.FunctionDef;\nimport edu.harvard.seas.pl.formulog.functions.UserFunctionDef;\nimport edu.harvard.seas.pl.formulog.symbols.BuiltInFunctionSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.FunctionSymbol;\nimport java.util.HashSet;\nimport java.util.Set;\n\npublic class SmtCallFinder {\n\n  private final Set<FunctionSymbol> smtCallSymbols = new HashSet<>();\n  private final Set<FunctionSymbol> visitedFunctions = new HashSet<>();\n\n  public SmtCallFinder() {\n    smtCallSymbols.add(BuiltInFunctionSymbol.IS_SAT);\n    smtCallSymbols.add(BuiltInFunctionSymbol.IS_SAT_OPT);\n    smtCallSymbols.add(BuiltInFunctionSymbol.IS_VALID);\n    smtCallSymbols.add(BuiltInFunctionSymbol.GET_MODEL);\n    smtCallSymbols.add(BuiltInFunctionSymbol.IS_SET_SAT);\n  }\n\n  public boolean containsSmtCall(Literal l) {\n    for (Term arg : l.getArgs()) {\n      if (arg.accept(tv, null)) {\n        return true;\n      }\n    }\n    return false;\n  }\n\n  private final TermVisitor<Void, Boolean> tv =\n      new TermVisitor<Void, Boolean>() {\n\n        @Override\n        public Boolean visit(Var t, Void in) {\n          return false;\n        }\n\n        @Override\n        public Boolean visit(Constructor c, Void in) {\n          for (Term arg : c.getArgs()) {\n            if (arg.accept(this, in)) {\n              return true;\n            }\n          }\n          return false;\n        }\n\n        @Override\n        public Boolean visit(Primitive<?> p, Void in) {\n          return false;\n        }\n\n        @Override\n        public Boolean visit(Expr e, Void in) {\n          return e.accept(ev, in);\n        }\n      };\n\n  private final ExprVisitor<Void, Boolean> ev =\n      new ExprVisitor<Void, Boolean>() {\n\n        @Override\n        public Boolean visit(MatchExpr matchExpr, Void in) {\n          if (matchExpr.getMatchee().accept(tv, in)) {\n            return true;\n          }\n          for (MatchClause match : matchExpr) {\n            if (match.getRhs().accept(tv, in)) {\n              return true;\n            }\n          }\n          return false;\n        }\n\n        @Override\n        public Boolean visit(FunctionCall funcCall, Void in) {\n          FunctionSymbol sym = funcCall.getSymbol();\n          if (smtCallSymbols.contains(sym)) {\n            return true;\n          }\n          for (Term arg : funcCall.getArgs()) {\n            if (arg.accept(tv, in)) {\n              return true;\n            }\n          }\n          FunctionDef def = funcCall.getFactory().getDefManager().lookup(sym);\n          if (def instanceof UserFunctionDef\n              && visitedFunctions.add(sym)\n              && ((UserFunctionDef) def).getBody().accept(tv, in)) {\n            smtCallSymbols.add(sym);\n            return true;\n          }\n          return false;\n        }\n\n        @Override\n        public Boolean visit(LetFunExpr funcDef, Void in) {\n          throw new AssertionError(\"impossible\");\n        }\n\n        @Override\n        public Boolean visit(Fold fold, Void in) {\n          return fold.getShamCall().accept(this, in);\n        }\n      };\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/eval/StratumEvaluator.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.eval;\n\npublic interface StratumEvaluator {\n\n  void evaluate() throws EvaluationException;\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/eval/UncheckedEvaluationException.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.eval;\n\npublic class UncheckedEvaluationException extends RuntimeException {\n\n  private static final long serialVersionUID = 3409298995077099693L;\n\n  public UncheckedEvaluationException() {}\n\n  public UncheckedEvaluationException(String message) {\n    super(message);\n  }\n\n  public UncheckedEvaluationException(Throwable cause) {\n    super(cause);\n  }\n\n  public UncheckedEvaluationException(String message, Throwable cause) {\n    super(message, cause);\n  }\n\n  public UncheckedEvaluationException(\n      String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {\n    super(message, cause, enableSuppression, writableStackTrace);\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/functions/BuiltInFunctionDefFactory.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.functions;\n\nimport edu.harvard.seas.pl.formulog.Configuration;\nimport edu.harvard.seas.pl.formulog.Main;\nimport edu.harvard.seas.pl.formulog.ast.*;\nimport edu.harvard.seas.pl.formulog.ast.Constructors.SolverVariable;\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport edu.harvard.seas.pl.formulog.smt.SmtLibSolver;\nimport edu.harvard.seas.pl.formulog.smt.SmtResult;\nimport edu.harvard.seas.pl.formulog.smt.SmtStatus;\nimport edu.harvard.seas.pl.formulog.symbols.BuiltInConstructorSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.BuiltInFunctionSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.ConstructorSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.FunctionSymbol;\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport edu.harvard.seas.pl.formulog.util.Triple;\nimport java.util.*;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.Future;\nimport java.util.function.BiFunction;\n\npublic final class BuiltInFunctionDefFactory {\n\n  private final SmtLibSolver smt;\n\n  public BuiltInFunctionDefFactory(SmtLibSolver smt) {\n    this.smt = smt;\n  }\n\n  public FunctionDef get(BuiltInFunctionSymbol sym) {\n    switch (sym) {\n      case I32_ADD:\n        return I32Add.INSTANCE;\n      case I32_SUB:\n        return I32Sub.INSTANCE;\n      case I32_MUL:\n        return I32Mul.INSTANCE;\n      case I32_SDIV:\n        return I32Sdiv.INSTANCE;\n      case I32_SREM:\n        return I32Srem.INSTANCE;\n      case I32_UDIV:\n        return i32Udiv;\n      case I32_UREM:\n        return i32Urem;\n      case I32_NEG:\n        return I32Neg.INSTANCE;\n      case I32_AND:\n        return I32And.INSTANCE;\n      case I32_OR:\n        return I32Or.INSTANCE;\n      case I32_XOR:\n        return I32Xor.INSTANCE;\n      case I32_GT:\n        return I32Gt.INSTANCE;\n      case I32_GE:\n        return I32Gte.INSTANCE;\n      case I32_LT:\n        return I32Lt.INSTANCE;\n      case I32_LE:\n        return I32Lte.INSTANCE;\n      case I32_SHL:\n        return i32Shl;\n      case I32_ASHR:\n        return i32Ashr;\n      case I32_LSHR:\n        return i32Lshr;\n      case I64_SHL:\n        return i64Shl;\n      case I64_ASHR:\n        return i64Ashr;\n      case I64_LSHR:\n        return i64Lshr;\n      case I64_ADD:\n        return I64Add.INSTANCE;\n      case I64_SUB:\n        return I64Sub.INSTANCE;\n      case I64_MUL:\n        return I64Mul.INSTANCE;\n      case I64_SDIV:\n        return I64Sdiv.INSTANCE;\n      case I64_SREM:\n        return I64Srem.INSTANCE;\n      case I64_UDIV:\n        return i64Udiv;\n      case I64_UREM:\n        return i64Urem;\n      case I64_NEG:\n        return I64Neg.INSTANCE;\n      case I64_AND:\n        return I64And.INSTANCE;\n      case I64_OR:\n        return I64Or.INSTANCE;\n      case I64_XOR:\n        return I64Xor.INSTANCE;\n      case I64_GT:\n        return I64Gt.INSTANCE;\n      case I64_GE:\n        return I64Gte.INSTANCE;\n      case I64_LT:\n        return I64Lt.INSTANCE;\n      case I64_LE:\n        return I64Lte.INSTANCE;\n      case FP32_ADD:\n        return FP32Add.INSTANCE;\n      case FP32_SUB:\n        return FP32Sub.INSTANCE;\n      case FP32_MUL:\n        return FP32Mul.INSTANCE;\n      case FP32_DIV:\n        return FP32Div.INSTANCE;\n      case FP32_REM:\n        return FP32Rem.INSTANCE;\n      case FP32_NEG:\n        return FP32Neg.INSTANCE;\n      case FP32_GT:\n        return FP32Gt.INSTANCE;\n      case FP32_GE:\n        return FP32Gte.INSTANCE;\n      case FP32_LT:\n        return FP32Lt.INSTANCE;\n      case FP32_LE:\n        return FP32Lte.INSTANCE;\n      case FP32_EQ:\n        return FP32Eq.INSTANCE;\n      case FP64_ADD:\n        return FP64Add.INSTANCE;\n      case FP64_SUB:\n        return FP64Sub.INSTANCE;\n      case FP64_MUL:\n        return FP64Mul.INSTANCE;\n      case FP64_DIV:\n        return FP64Div.INSTANCE;\n      case FP64_REM:\n        return FP64Rem.INSTANCE;\n      case FP64_NEG:\n        return FP64Neg.INSTANCE;\n      case FP64_GT:\n        return FP64Gt.INSTANCE;\n      case FP64_GE:\n        return FP64Gte.INSTANCE;\n      case FP64_LT:\n        return FP64Lt.INSTANCE;\n      case FP64_LE:\n        return FP64Lte.INSTANCE;\n      case FP64_EQ:\n        return FP64Eq.INSTANCE;\n      case BEQ:\n        return Beq.INSTANCE;\n      case BNEQ:\n        return Bneq.INSTANCE;\n      case BNOT:\n        return bnot;\n      case TO_STRING:\n        return ToString.INSTANCE;\n      case STRING_CMP:\n        return StringCmp.INSTANCE;\n      case I32_SCMP:\n        return I32Scmp.INSTANCE;\n      case I32_UCMP:\n        return I32Ucmp.INSTANCE;\n      case I64_SCMP:\n        return I64Scmp.INSTANCE;\n      case I64_UCMP:\n        return I64Ucmp.INSTANCE;\n      case STRING_CONCAT:\n        return StringConcat.INSTANCE;\n      case STRING_MATCHES:\n        return stringMatches;\n      case STRING_STARTS_WITH:\n        return stringStartsWith;\n      case STRING_TO_LIST:\n        return stringToList;\n      case LIST_TO_STRING:\n        return listToString;\n      case CHAR_AT:\n        return charAt;\n      case SUBSTRING:\n        return substring;\n      case STRING_LENGTH:\n        return stringLength;\n      case IS_SAT:\n        return isSat;\n      case IS_SAT_OPT:\n        return isSatOpt;\n      case IS_SET_SAT:\n        return isSetSat;\n      case IS_VALID:\n        return isValid;\n      case GET_MODEL:\n        return getModel;\n      case QUERY_MODEL:\n        return QueryModel.INSTANCE;\n      case fp32ToFp64:\n        return PrimitiveConversions.fp32ToFp64;\n      case fp32ToI32:\n        return PrimitiveConversions.fp32ToI32;\n      case fp32ToI64:\n        return PrimitiveConversions.fp32ToI64;\n      case fp64ToFp32:\n        return PrimitiveConversions.fp64ToFp32;\n      case fp64ToI32:\n        return PrimitiveConversions.fp64ToI32;\n      case fp64ToI64:\n        return PrimitiveConversions.fp64ToI64;\n      case i32ToFp32:\n        return PrimitiveConversions.i32ToFp32;\n      case i32ToFp64:\n        return PrimitiveConversions.i32ToFp64;\n      case i32ToI64:\n        return PrimitiveConversions.i32ToI64;\n      case i64ToFp32:\n        return PrimitiveConversions.i64ToFp32;\n      case i64ToFp64:\n        return PrimitiveConversions.i64ToFp64;\n      case i64ToI32:\n        return PrimitiveConversions.i64ToI32;\n      case stringToI32:\n        return PrimitiveConversions.stringToI32;\n      case stringToI64:\n        return PrimitiveConversions.stringToI64;\n      case PRINT:\n        return Print.INSTANCE;\n      case OPAQUE_SET_CHOOSE:\n        return OpaqueSetOps.choose;\n      case OPAQUE_SET_DIFF:\n        return OpaqueSetOps.diff;\n      case OPAQUE_SET_EMPTY:\n        return OpaqueSetOps.empty;\n      case OPAQUE_SET_MINUS:\n        return OpaqueSetOps.minus;\n      case OPAQUE_SET_PLUS:\n        return OpaqueSetOps.plus;\n      case OPAQUE_SET_SIZE:\n        return OpaqueSetOps.size;\n      case OPAQUE_SET_UNION:\n        return OpaqueSetOps.union;\n      case OPAQUE_SET_MEMBER:\n        return OpaqueSetOps.member;\n      case OPAQUE_SET_SINGLETON:\n        return OpaqueSetOps.singleton;\n      case OPAQUE_SET_SUBSET:\n        return OpaqueSetOps.subset;\n      case OPAQUE_SET_FROM_LIST:\n        return OpaqueSetOps.fromList;\n    }\n    throw new AssertionError();\n  }\n\n  private enum I32Add implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.I32_ADD;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      I32 arg1 = (I32) args[0];\n      I32 arg2 = (I32) args[1];\n      return I32.make(arg1.getVal() + arg2.getVal());\n    }\n  }\n\n  private enum I32Sub implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.I32_SUB;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      I32 arg1 = (I32) args[0];\n      I32 arg2 = (I32) args[1];\n      return I32.make(arg1.getVal() - arg2.getVal());\n    }\n  }\n\n  private enum I32Mul implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.I32_MUL;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      I32 arg1 = (I32) args[0];\n      I32 arg2 = (I32) args[1];\n      return I32.make(arg1.getVal() * arg2.getVal());\n    }\n  }\n\n  private enum I32Sdiv implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.I32_SDIV;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      I32 arg1 = (I32) args[0];\n      I32 arg2 = (I32) args[1];\n      if (arg2.getVal() == 0) {\n        throw new EvaluationException(\"Division by zero\");\n      }\n      return I32.make(arg1.getVal() / arg2.getVal());\n    }\n  }\n\n  private enum I32Srem implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.I32_SREM;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      I32 arg1 = (I32) args[0];\n      I32 arg2 = (I32) args[1];\n      if (arg2.getVal() == 0) {\n        throw new EvaluationException(\"Remainder by zero\");\n      }\n      return I32.make(arg1.getVal() % arg2.getVal());\n    }\n  }\n\n  private static final FunctionDef i32Udiv =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.I32_UDIV;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          I32 arg1 = (I32) args[0];\n          I32 arg2 = (I32) args[1];\n          if (arg2.getVal() == 0) {\n            throw new EvaluationException(\"Division by zero\");\n          }\n          return I32.make(Integer.divideUnsigned(arg1.getVal(), arg2.getVal()));\n        }\n      };\n\n  private static final FunctionDef i32Urem =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.I32_UREM;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          I32 arg1 = (I32) args[0];\n          I32 arg2 = (I32) args[1];\n          if (arg2.getVal() == 0) {\n            throw new EvaluationException(\"Remainder by zero\");\n          }\n          return I32.make(Integer.remainderUnsigned(arg1.getVal(), arg2.getVal()));\n        }\n      };\n\n  private enum I32Gt implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.I32_GT;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      I32 arg1 = (I32) args[0];\n      I32 arg2 = (I32) args[1];\n      return boolToBoolTerm(arg1.getVal() > arg2.getVal());\n    }\n  }\n\n  private enum I32Gte implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.I32_GE;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      I32 arg1 = (I32) args[0];\n      I32 arg2 = (I32) args[1];\n      return boolToBoolTerm(arg1.getVal() >= arg2.getVal());\n    }\n  }\n\n  private enum I32Lt implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.I32_LT;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      I32 arg1 = (I32) args[0];\n      I32 arg2 = (I32) args[1];\n      return boolToBoolTerm(arg1.getVal() < arg2.getVal());\n    }\n  }\n\n  private enum I32Lte implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.I32_LE;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      I32 arg1 = (I32) args[0];\n      I32 arg2 = (I32) args[1];\n      return boolToBoolTerm(arg1.getVal() <= arg2.getVal());\n    }\n  }\n\n  private enum I32And implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.I32_AND;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      I32 arg1 = (I32) args[0];\n      I32 arg2 = (I32) args[1];\n      return I32.make(arg1.getVal() & arg2.getVal());\n    }\n  }\n\n  private enum I32Or implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.I32_OR;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      I32 arg1 = (I32) args[0];\n      I32 arg2 = (I32) args[1];\n      return I32.make(arg1.getVal() | arg2.getVal());\n    }\n  }\n\n  private enum I32Xor implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.I32_XOR;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      I32 arg1 = (I32) args[0];\n      I32 arg2 = (I32) args[1];\n      return I32.make(arg1.getVal() ^ arg2.getVal());\n    }\n  }\n\n  private enum I32Neg implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.I32_NEG;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      I32 arg1 = (I32) args[0];\n      return I32.make(-arg1.getVal());\n    }\n  }\n\n  private static final FunctionDef i32Shl =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.I32_SHL;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          I32 arg1 = (I32) args[0];\n          I32 arg2 = (I32) args[1];\n          return I32.make(arg1.getVal() << arg2.getVal());\n        }\n      };\n\n  private static final FunctionDef i32Ashr =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.I32_ASHR;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          I32 arg1 = (I32) args[0];\n          I32 arg2 = (I32) args[1];\n          return I32.make(arg1.getVal() >> arg2.getVal());\n        }\n      };\n\n  private static final FunctionDef i32Lshr =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.I32_LSHR;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          I32 arg1 = (I32) args[0];\n          I32 arg2 = (I32) args[1];\n          return I32.make(arg1.getVal() >>> arg2.getVal());\n        }\n      };\n\n  private static final FunctionDef i64Shl =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.I64_SHL;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          I64 arg1 = (I64) args[0];\n          I64 arg2 = (I64) args[1];\n          return I64.make(arg1.getVal() << arg2.getVal());\n        }\n      };\n\n  private static final FunctionDef i64Ashr =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.I64_ASHR;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          I64 arg1 = (I64) args[0];\n          I64 arg2 = (I64) args[1];\n          return I64.make(arg1.getVal() >> arg2.getVal());\n        }\n      };\n\n  private static final FunctionDef i64Lshr =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.I64_LSHR;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          I64 arg1 = (I64) args[0];\n          I64 arg2 = (I64) args[1];\n          return I64.make(arg1.getVal() >>> arg2.getVal());\n        }\n      };\n\n  private enum I64Add implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.I64_ADD;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      I64 arg1 = (I64) args[0];\n      I64 arg2 = (I64) args[1];\n      return I64.make(arg1.getVal() + arg2.getVal());\n    }\n  }\n\n  private enum I64Sub implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.I64_SUB;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      I64 arg1 = (I64) args[0];\n      I64 arg2 = (I64) args[1];\n      return I64.make(arg1.getVal() - arg2.getVal());\n    }\n  }\n\n  private enum I64Mul implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.I64_MUL;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      I64 arg1 = (I64) args[0];\n      I64 arg2 = (I64) args[1];\n      return I64.make(arg1.getVal() * arg2.getVal());\n    }\n  }\n\n  private enum I64Sdiv implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.I64_SDIV;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      I64 arg1 = (I64) args[0];\n      I64 arg2 = (I64) args[1];\n      if (arg2.getVal() == 0) {\n        throw new EvaluationException(\"Division by zero\");\n      }\n      return I64.make(arg1.getVal() / arg2.getVal());\n    }\n  }\n\n  private enum I64Srem implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.I64_SREM;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      I64 arg1 = (I64) args[0];\n      I64 arg2 = (I64) args[1];\n      if (arg2.getVal() == 0) {\n        throw new EvaluationException(\"Remainder by zero\");\n      }\n      return I64.make(arg1.getVal() % arg2.getVal());\n    }\n  }\n\n  private static final FunctionDef i64Udiv =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.I64_UDIV;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          I64 arg1 = (I64) args[0];\n          I64 arg2 = (I64) args[1];\n          if (arg2.getVal() == 0) {\n            throw new EvaluationException(\"Division by zero\");\n          }\n          return I64.make(Long.divideUnsigned(arg1.getVal(), arg2.getVal()));\n        }\n      };\n\n  private static final FunctionDef i64Urem =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.I64_UREM;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          I64 arg1 = (I64) args[0];\n          I64 arg2 = (I64) args[1];\n          if (arg2.getVal() == 0) {\n            throw new EvaluationException(\"Remainder by zero\");\n          }\n          return I64.make(Long.remainderUnsigned(arg1.getVal(), arg2.getVal()));\n        }\n      };\n\n  private enum I64Gt implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.I64_GT;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      I64 arg1 = (I64) args[0];\n      I64 arg2 = (I64) args[1];\n      return boolToBoolTerm(arg1.getVal() > arg2.getVal());\n    }\n  }\n\n  private enum I64Gte implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.I64_GE;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      I64 arg1 = (I64) args[0];\n      I64 arg2 = (I64) args[1];\n      return boolToBoolTerm(arg1.getVal() >= arg2.getVal());\n    }\n  }\n\n  private enum I64Lt implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.I64_LT;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      I64 arg1 = (I64) args[0];\n      I64 arg2 = (I64) args[1];\n      return boolToBoolTerm(arg1.getVal() < arg2.getVal());\n    }\n  }\n\n  private enum I64Lte implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.I64_LE;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      I64 arg1 = (I64) args[0];\n      I64 arg2 = (I64) args[1];\n      return boolToBoolTerm(arg1.getVal() <= arg2.getVal());\n    }\n  }\n\n  private enum I64And implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.I64_AND;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      I64 arg1 = (I64) args[0];\n      I64 arg2 = (I64) args[1];\n      return I64.make(arg1.getVal() & arg2.getVal());\n    }\n  }\n\n  private enum I64Or implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.I64_OR;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      I64 arg1 = (I64) args[0];\n      I64 arg2 = (I64) args[1];\n      return I64.make(arg1.getVal() | arg2.getVal());\n    }\n  }\n\n  private enum I64Xor implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.I64_XOR;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      I64 arg1 = (I64) args[0];\n      I64 arg2 = (I64) args[1];\n      return I64.make(arg1.getVal() ^ arg2.getVal());\n    }\n  }\n\n  private enum I64Neg implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.I64_NEG;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      I64 arg1 = (I64) args[0];\n      return I64.make(-arg1.getVal());\n    }\n  }\n\n  private enum FP32Add implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.FP32_ADD;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      FP32 arg1 = (FP32) args[0];\n      FP32 arg2 = (FP32) args[1];\n      return FP32.make(arg1.getVal() + arg2.getVal());\n    }\n  }\n\n  private enum FP32Sub implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.FP32_SUB;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      FP32 arg1 = (FP32) args[0];\n      FP32 arg2 = (FP32) args[1];\n      return FP32.make(arg1.getVal() - arg2.getVal());\n    }\n  }\n\n  private enum FP32Mul implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.FP32_MUL;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      FP32 arg1 = (FP32) args[0];\n      FP32 arg2 = (FP32) args[1];\n      return FP32.make(arg1.getVal() * arg2.getVal());\n    }\n  }\n\n  private enum FP32Div implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.FP32_DIV;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      FP32 arg1 = (FP32) args[0];\n      FP32 arg2 = (FP32) args[1];\n      if (arg2.getVal() == 0) {\n        throw new EvaluationException(\"Division by zero\");\n      }\n      return FP32.make(arg1.getVal() / arg2.getVal());\n    }\n  }\n\n  private enum FP32Rem implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.FP32_REM;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      FP32 arg1 = (FP32) args[0];\n      FP32 arg2 = (FP32) args[1];\n      if (arg2.getVal() == 0) {\n        throw new EvaluationException(\"Remainder by zero\");\n      }\n      return FP32.make(arg1.getVal() % arg2.getVal());\n    }\n  }\n\n  private enum FP32Gt implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.FP32_GT;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      FP32 arg1 = (FP32) args[0];\n      FP32 arg2 = (FP32) args[1];\n      return boolToBoolTerm(arg1.getVal() > arg2.getVal());\n    }\n  }\n\n  private enum FP32Gte implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.FP32_GE;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      FP32 arg1 = (FP32) args[0];\n      FP32 arg2 = (FP32) args[1];\n      return boolToBoolTerm(arg1.getVal() >= arg2.getVal());\n    }\n  }\n\n  private enum FP32Lt implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.FP32_LT;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      FP32 arg1 = (FP32) args[0];\n      FP32 arg2 = (FP32) args[1];\n      return boolToBoolTerm(arg1.getVal() < arg2.getVal());\n    }\n  }\n\n  private enum FP32Lte implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.FP32_LE;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      FP32 arg1 = (FP32) args[0];\n      FP32 arg2 = (FP32) args[1];\n      return boolToBoolTerm(arg1.getVal() <= arg2.getVal());\n    }\n  }\n\n  private enum FP32Eq implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.FP32_EQ;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      FP32 arg1 = (FP32) args[0];\n      FP32 arg2 = (FP32) args[1];\n      return boolToBoolTerm(arg1.getVal().floatValue() == arg2.getVal().floatValue());\n    }\n  }\n\n  private enum FP32Neg implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.FP32_NEG;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      FP32 arg1 = (FP32) args[0];\n      return FP32.make(-arg1.getVal());\n    }\n  }\n\n  private enum FP64Add implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.FP64_ADD;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      FP64 arg1 = (FP64) args[0];\n      FP64 arg2 = (FP64) args[1];\n      return FP64.make(arg1.getVal() + arg2.getVal());\n    }\n  }\n\n  private enum FP64Sub implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.FP64_SUB;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      FP64 arg1 = (FP64) args[0];\n      FP64 arg2 = (FP64) args[1];\n      return FP64.make(arg1.getVal() - arg2.getVal());\n    }\n  }\n\n  private enum FP64Mul implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.FP64_MUL;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      FP64 arg1 = (FP64) args[0];\n      FP64 arg2 = (FP64) args[1];\n      return FP64.make(arg1.getVal() * arg2.getVal());\n    }\n  }\n\n  private enum FP64Div implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.FP64_DIV;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      FP64 arg1 = (FP64) args[0];\n      FP64 arg2 = (FP64) args[1];\n      if (arg2.getVal() == 0) {\n        throw new EvaluationException(\"Division by zero\");\n      }\n      return FP64.make(arg1.getVal() / arg2.getVal());\n    }\n  }\n\n  private enum FP64Rem implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.FP64_REM;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      FP64 arg1 = (FP64) args[0];\n      FP64 arg2 = (FP64) args[1];\n      if (arg2.getVal() == 0) {\n        throw new EvaluationException(\"Remainder by zero\");\n      }\n      return FP64.make(arg1.getVal() % arg2.getVal());\n    }\n  }\n\n  private enum FP64Gt implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.FP64_GT;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      FP64 arg1 = (FP64) args[0];\n      FP64 arg2 = (FP64) args[1];\n      return boolToBoolTerm(arg1.getVal() > arg2.getVal());\n    }\n  }\n\n  private enum FP64Gte implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.FP64_GE;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      FP64 arg1 = (FP64) args[0];\n      FP64 arg2 = (FP64) args[1];\n      return boolToBoolTerm(arg1.getVal() >= arg2.getVal());\n    }\n  }\n\n  private enum FP64Lt implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.FP64_LT;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      FP64 arg1 = (FP64) args[0];\n      FP64 arg2 = (FP64) args[1];\n      return boolToBoolTerm(arg1.getVal() < arg2.getVal());\n    }\n  }\n\n  private enum FP64Lte implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.FP64_LE;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      FP64 arg1 = (FP64) args[0];\n      FP64 arg2 = (FP64) args[1];\n      return boolToBoolTerm(arg1.getVal() <= arg2.getVal());\n    }\n  }\n\n  private enum FP64Eq implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.FP64_EQ;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      FP64 arg1 = (FP64) args[0];\n      FP64 arg2 = (FP64) args[1];\n      return boolToBoolTerm(arg1.getVal().doubleValue() == arg2.getVal().doubleValue());\n    }\n  }\n\n  private enum FP64Neg implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.FP64_NEG;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      FP64 arg1 = (FP64) args[0];\n      return FP64.make(-arg1.getVal());\n    }\n  }\n\n  private enum Beq implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.BEQ;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      Term arg1 = args[0];\n      Term arg2 = args[1];\n      return boolToBoolTerm(arg1.equals(arg2));\n    }\n  }\n\n  private enum Bneq implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.BNEQ;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      Term arg1 = args[0];\n      Term arg2 = args[1];\n      return boolToBoolTerm(!arg1.equals(arg2));\n    }\n  }\n\n  private static final FunctionDef bnot =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.BNOT;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          Term arg = args[0];\n          if (arg.equals(trueTerm)) {\n            return falseTerm;\n          }\n          return trueTerm;\n        }\n      };\n\n  private enum ToString implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.TO_STRING;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      Term arg = args[0];\n      if (arg instanceof StringTerm) {\n        return arg;\n      }\n      return StringTerm.make(args[0].toString());\n    }\n  }\n\n  private enum StringCmp implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.STRING_CMP;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      String s1 = ((StringTerm) args[0]).getVal();\n      String s2 = ((StringTerm) args[1]).getVal();\n      return makeCmp(s1, s2, (x, y) -> x.compareTo(y));\n    }\n  }\n\n  private enum I32Scmp implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.I32_SCMP;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      int x = ((I32) args[0]).getVal();\n      int y = ((I32) args[1]).getVal();\n      return makeCmp(x, y, Integer::compare);\n    }\n  }\n\n  private enum I32Ucmp implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.I32_UCMP;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      int x = ((I32) args[0]).getVal();\n      int y = ((I32) args[1]).getVal();\n      return makeCmp(x, y, Integer::compareUnsigned);\n    }\n  }\n\n  private enum I64Scmp implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.I64_SCMP;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      long x = ((I64) args[0]).getVal();\n      long y = ((I64) args[1]).getVal();\n      return makeCmp(x, y, Long::compare);\n    }\n  }\n\n  private enum I64Ucmp implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.I64_UCMP;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      long x = ((I64) args[0]).getVal();\n      long y = ((I64) args[1]).getVal();\n      return makeCmp(x, y, Long::compareUnsigned);\n    }\n  }\n\n  private enum StringConcat implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.STRING_CONCAT;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      String s1 = ((StringTerm) args[0]).getVal();\n      String s2 = ((StringTerm) args[1]).getVal();\n      return StringTerm.make(s1 + s2);\n    }\n  }\n\n  private static final FunctionDef stringMatches =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.STRING_MATCHES;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          String str = ((StringTerm) args[0]).getVal();\n          String re = ((StringTerm) args[1]).getVal();\n          return boolToBoolTerm(str.matches(re));\n        }\n      };\n\n  private static final FunctionDef stringStartsWith =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.STRING_STARTS_WITH;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          String str = ((StringTerm) args[0]).getVal();\n          String pre = ((StringTerm) args[1]).getVal();\n          return boolToBoolTerm(str.startsWith(pre));\n        }\n      };\n\n  private static final FunctionDef stringToList =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.STRING_TO_LIST;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          String s = ((StringTerm) args[0]).getVal();\n          List<I32> l = new ArrayList<>();\n          for (int i = 0; i < s.length(); ++i) {\n            l.add(I32.make(s.charAt(i)));\n          }\n          return Terms.termListToTerm(l);\n        }\n      };\n\n  private static final FunctionDef listToString =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.LIST_TO_STRING;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          List<I32> l = Terms.termToTermList(args[0]);\n          char[] cs = new char[l.size()];\n          int i = 0;\n          for (I32 c : l) {\n            cs[i] = (char) c.getVal().intValue();\n            i++;\n          }\n          return StringTerm.make(new String(cs));\n        }\n      };\n\n  private static final FunctionDef charAt =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.CHAR_AT;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          String s = ((StringTerm) args[0]).getVal();\n          int i = ((I32) args[1]).getVal();\n          if (i < 0 || i >= s.length()) {\n            return Constructors.none();\n          }\n          return Constructors.some(I32.make(s.charAt(i)));\n        }\n      };\n\n  private static final FunctionDef substring =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.SUBSTRING;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          String s = ((StringTerm) args[0]).getVal();\n          int i = ((I32) args[1]).getVal();\n          int j = ((I32) args[2]).getVal();\n          if (i < 0 || j > s.length() || i > j) {\n            return Constructors.none();\n          }\n          return Constructors.some(StringTerm.make(s.substring(i, j)));\n        }\n      };\n\n  private static final FunctionDef stringLength =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.STRING_LENGTH;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          String s = ((StringTerm) args[0]).getVal();\n          return I32.make(s.length());\n        }\n      };\n\n  private final Map<Triple<Set<SmtLibTerm>, Boolean, Integer>, Future<SmtResult>> smtMemo =\n      new ConcurrentHashMap<>();\n\n  private Pair<SmtStatus, Model> querySmt(SmtLibTerm assertions, boolean getModel)\n      throws EvaluationException {\n    return querySmt(assertions, getModel, Integer.MAX_VALUE);\n  }\n\n  private Pair<SmtStatus, Model> querySmt(SmtLibTerm assertions, boolean getModel, int timeout)\n      throws EvaluationException {\n    return querySmt(breakIntoConjuncts(assertions), getModel, timeout);\n  }\n\n  private List<SmtLibTerm> breakIntoConjuncts(SmtLibTerm assertion) {\n    List<SmtLibTerm> l = new ArrayList<>();\n    breakIntoConjuncts(assertion, l);\n    return l;\n  }\n\n  private void breakIntoConjunctsNegated(SmtLibTerm assertion, List<SmtLibTerm> acc) {\n    if (assertion instanceof Constructor) {\n      Constructor c = (Constructor) assertion;\n      ConstructorSymbol sym = c.getSymbol();\n      Term[] args = c.getArgs();\n      if (sym.equals(BuiltInConstructorSymbol.SMT_NOT)) {\n        // Turn ~~A into A\n        breakIntoConjuncts((SmtLibTerm) args[0], acc);\n        return;\n      } else if (sym.equals(BuiltInConstructorSymbol.SMT_IMP)) {\n        // Turn ~(A => B) into A /\\ ~B\n        breakIntoConjuncts((SmtLibTerm) args[0], acc);\n        breakIntoConjunctsNegated((SmtLibTerm) args[1], acc);\n        return;\n      } else if (sym.equals(BuiltInConstructorSymbol.SMT_OR)) {\n        // Turn ~(A \\/ B) into ~A /\\ ~B\n        breakIntoConjunctsNegated((SmtLibTerm) args[0], acc);\n        breakIntoConjunctsNegated((SmtLibTerm) args[1], acc);\n        return;\n      }\n    } else if (assertion.equals(trueTerm)) {\n      // Turn ~True into False\n      acc.add(falseTerm);\n      return;\n    }\n    if (!assertion.equals(falseTerm)) {\n      acc.add(negate(assertion));\n    }\n  }\n\n  private void breakIntoConjuncts(SmtLibTerm assertion, List<SmtLibTerm> acc) {\n    if (assertion instanceof Constructor) {\n      Constructor c = (Constructor) assertion;\n      ConstructorSymbol sym = c.getSymbol();\n      Term[] args = c.getArgs();\n      if (sym.equals(BuiltInConstructorSymbol.SMT_AND)) {\n        breakIntoConjuncts((SmtLibTerm) args[0], acc);\n        breakIntoConjuncts((SmtLibTerm) args[1], acc);\n        return;\n      } else if (sym.equals(BuiltInConstructorSymbol.SMT_NOT)) {\n        breakIntoConjunctsNegated((SmtLibTerm) args[0], acc);\n        return;\n      }\n    }\n    if (!assertion.equals(trueTerm)) {\n      acc.add(assertion);\n    }\n  }\n\n  private SmtLibTerm negate(Term t) {\n    return Constructors.make(BuiltInConstructorSymbol.SMT_NOT, Terms.singletonArray(t));\n  }\n\n  private Pair<SmtStatus, Model> querySmt(\n      Collection<SmtLibTerm> assertions, boolean getModel, int timeout) throws EvaluationException {\n    long start = System.nanoTime();\n    try {\n      if (timeout < 0) {\n        timeout = -1;\n      }\n      Set<SmtLibTerm> set = new LinkedHashSet<>(assertions);\n      if (set.contains(BoolTerm.mkFalse())) {\n        return new Pair<>(SmtStatus.UNSATISFIABLE, null);\n      }\n      set.remove(BoolTerm.mkTrue());\n      if (set.isEmpty()) {\n        Model m = getModel ? Model.make(Collections.emptyMap()) : null;\n        return new Pair<>(SmtStatus.SATISFIABLE, m);\n      }\n      SmtResult res;\n      if (Configuration.smtMemoize) {\n        res = querySmtWithMemo(set, getModel, timeout);\n      } else {\n        res = smt.check(set, getModel, timeout);\n      }\n      return new Pair<>(res.status, res.model);\n    } finally {\n      Configuration.recordSmtTime(System.nanoTime() - start);\n    }\n  }\n\n  private SmtResult querySmtWithMemo(Set<SmtLibTerm> assertions, boolean getModel, int timeout)\n      throws EvaluationException {\n    Triple<Set<SmtLibTerm>, Boolean, Integer> key = new Triple<>(assertions, getModel, timeout);\n    CompletableFuture<SmtResult> completableFut = new CompletableFuture<>();\n    Future<SmtResult> fut = smtMemo.putIfAbsent(key, completableFut);\n    if (fut == null) {\n      completableFut.complete(smt.check(assertions, getModel, timeout));\n      fut = completableFut;\n    }\n    long waitStart = 0;\n    if (Configuration.timeSmt || Main.smtStats) {\n      waitStart = System.nanoTime();\n    }\n    try {\n      return fut.get();\n    } catch (InterruptedException | ExecutionException e) {\n      throw new EvaluationException(e);\n    } finally {\n      if (Configuration.timeSmt || Main.smtStats) {\n        Configuration.recordSmtWaitTime(System.nanoTime() - waitStart);\n      }\n    }\n  }\n\n  private final FunctionDef isSat =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.IS_SAT;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          SmtLibTerm formula = (SmtLibTerm) args[0];\n          Pair<SmtStatus, Model> p = querySmt(formula, false);\n          switch (p.fst()) {\n            case SATISFIABLE:\n              return trueTerm;\n            case UNKNOWN:\n              throw new EvaluationException(\"Z3 returned \\\"unknown\\\"\");\n            case UNSATISFIABLE:\n              return falseTerm;\n          }\n          throw new AssertionError(\"impossible\");\n        }\n      };\n\n  private final FunctionDef isSatOpt =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.IS_SAT_OPT;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          SmtLibTerm formula = (SmtLibTerm) args[0];\n          List<SmtLibTerm> assertions = Terms.termToTermList(formula);\n          Collections.reverse(assertions);\n          Constructor timeoutOpt = (Constructor) args[1];\n          Integer timeout = extractOptionalTimeout(timeoutOpt);\n          Pair<SmtStatus, Model> p = querySmt(assertions, false, timeout);\n          switch (p.fst()) {\n            case SATISFIABLE:\n              return Constructors.some(trueTerm);\n            case UNKNOWN:\n              return Constructors.none();\n            case UNSATISFIABLE:\n              return Constructors.some(falseTerm);\n          }\n          throw new AssertionError(\"impossible\");\n        }\n      };\n\n  private final FunctionDef isSetSat =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.IS_SET_SAT;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          OpaqueSet formula = (OpaqueSet) args[0];\n          Constructor timeoutOpt = (Constructor) args[1];\n          Integer timeout = extractOptionalTimeout(timeoutOpt);\n          @SuppressWarnings({\"unchecked\", \"rawtypes\"})\n          Pair<SmtStatus, Model> p = querySmt((Collection) formula.getCollection(), false, timeout);\n          switch (p.fst()) {\n            case SATISFIABLE:\n              return Constructors.some(trueTerm);\n            case UNKNOWN:\n              return Constructors.none();\n            case UNSATISFIABLE:\n              return Constructors.some(falseTerm);\n          }\n          throw new AssertionError(\"impossible\");\n        }\n      };\n\n  private final FunctionDef isValid =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.IS_VALID;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          SmtLibTerm formula = negate((SmtLibTerm) args[0]);\n          Pair<SmtStatus, Model> p = querySmt(formula, false);\n          switch (p.fst()) {\n            case SATISFIABLE:\n              return falseTerm;\n            case UNKNOWN:\n              throw new EvaluationException(\"Z3 returned \\\"unknown\\\"\");\n            case UNSATISFIABLE:\n              return trueTerm;\n          }\n          throw new AssertionError(\"impossible\");\n        }\n      };\n\n  private static int extractOptionalTimeout(Constructor opt) {\n    if (opt.getSymbol().equals(BuiltInConstructorSymbol.SOME)) {\n      return ((I32) opt.getArgs()[0]).getVal();\n    }\n    return Integer.MAX_VALUE;\n  }\n\n  private final FunctionDef getModel =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.GET_MODEL;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          List<SmtLibTerm> assertions = Terms.termToTermList((SmtLibTerm) args[0]);\n          Collections.reverse(assertions);\n          Constructor timeoutOpt = (Constructor) args[1];\n          Integer timeout = extractOptionalTimeout(timeoutOpt);\n          Pair<SmtStatus, Model> p = querySmt(assertions, true, timeout);\n          Model model = p.snd();\n          return model == null ? Constructors.none() : Constructors.some(model);\n        }\n      };\n\n  private enum QueryModel implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.QUERY_MODEL;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      SolverVariable x = (SolverVariable) args[0];\n      Model m = (Model) args[1];\n      Term t = m.getVal().get(x);\n      return t == null ? Constructors.none() : Constructors.some(t);\n    }\n  }\n\n  private static final SmtLibTerm trueTerm = BoolTerm.mkTrue();\n  private static final SmtLibTerm falseTerm = BoolTerm.mkFalse();\n\n  private static Term boolToBoolTerm(boolean b) {\n    if (b) {\n      return trueTerm;\n    } else {\n      return falseTerm;\n    }\n  }\n\n  private enum Print implements FunctionDef {\n    INSTANCE;\n\n    @Override\n    public FunctionSymbol getSymbol() {\n      return BuiltInFunctionSymbol.PRINT;\n    }\n\n    @Override\n    public Term evaluate(Term[] args) throws EvaluationException {\n      System.out.println(args[0]);\n      return trueTerm;\n    }\n  }\n\n  private static <T> Term makeCmp(T x, T y, BiFunction<T, T, Integer> cmp) {\n    int z = cmp.apply(x, y);\n    if (z < 0) {\n      return Constructors.makeZeroAry(BuiltInConstructorSymbol.CMP_LT);\n    } else if (z > 0) {\n      return Constructors.makeZeroAry(BuiltInConstructorSymbol.CMP_GT);\n    }\n    return Constructors.makeZeroAry(BuiltInConstructorSymbol.CMP_EQ);\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/functions/DummyFunctionDef.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.functions;\n\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport edu.harvard.seas.pl.formulog.symbols.FunctionSymbol;\n\npublic class DummyFunctionDef implements FunctionDef {\n\n  private final FunctionSymbol sym;\n  private volatile FunctionDef def;\n\n  public DummyFunctionDef(FunctionSymbol sym) {\n    this.sym = sym;\n  }\n\n  @Override\n  public FunctionSymbol getSymbol() {\n    return sym;\n  }\n\n  @Override\n  public Term evaluate(Term[] args) throws EvaluationException {\n    if (def == null) {\n      throw new EvaluationException();\n    }\n    return def.evaluate(args);\n  }\n\n  public void setDef(FunctionDef def) {\n    this.def = def;\n  }\n\n  public Object getDef() {\n    return def;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/functions/FunctionDef.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.functions;\n\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport edu.harvard.seas.pl.formulog.symbols.FunctionSymbol;\n\npublic interface FunctionDef {\n\n  FunctionSymbol getSymbol();\n\n  Term evaluate(Term[] args) throws EvaluationException;\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/functions/FunctionDefManager.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.functions;\n\nimport edu.harvard.seas.pl.formulog.smt.SmtLibSolver;\nimport edu.harvard.seas.pl.formulog.symbols.BuiltInFunctionSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.FunctionSymbol;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\npublic class FunctionDefManager {\n\n  private final Map<FunctionSymbol, FunctionDef> memo = new HashMap<>();\n\n  public FunctionDefManager() {\n    for (BuiltInFunctionSymbol sym : BuiltInFunctionSymbol.values()) {\n      memo.put(sym, new DummyFunctionDef(sym));\n    }\n  }\n\n  public void register(FunctionDef def) {\n    if (memo.put(def.getSymbol(), def) != null) {\n      throw new IllegalArgumentException(\n          \"Cannot register multiple definitions for the same function: \" + def.getSymbol());\n    }\n  }\n\n  public void reregister(FunctionDef def) {\n    if (memo.put(def.getSymbol(), def) == null) {\n      throw new IllegalArgumentException(\n          \"Expected there to already be a definition for function \" + def.getSymbol());\n    }\n  }\n\n  public FunctionDef lookup(FunctionSymbol symbol) {\n    FunctionDef def = memo.get(symbol);\n    if (def == null) {\n      throw new IllegalArgumentException(\"No function defined for symbol: \" + symbol);\n    }\n    return def;\n  }\n\n  public boolean hasDefinition(FunctionSymbol sym) {\n    return memo.containsKey(sym);\n  }\n\n  public Set<FunctionSymbol> getFunctionSymbols() {\n    return Collections.unmodifiableSet(new HashSet<>(memo.keySet()));\n  }\n\n  public void loadBuiltInFunctions(SmtLibSolver smt) {\n    BuiltInFunctionDefFactory builtIns = new BuiltInFunctionDefFactory(smt);\n    for (BuiltInFunctionSymbol sym : BuiltInFunctionSymbol.values()) {\n      memo.put(sym, builtIns.get(sym));\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/functions/OpaqueSetOps.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2021-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.functions;\n\nimport edu.harvard.seas.pl.formulog.ast.BoolTerm;\nimport edu.harvard.seas.pl.formulog.ast.Constructors;\nimport edu.harvard.seas.pl.formulog.ast.I32;\nimport edu.harvard.seas.pl.formulog.ast.OpaqueSet;\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.ast.Terms;\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport edu.harvard.seas.pl.formulog.symbols.BuiltInFunctionSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.FunctionSymbol;\nimport edu.harvard.seas.pl.formulog.util.Pair;\n\npublic final class OpaqueSetOps {\n\n  private OpaqueSetOps() {\n    throw new AssertionError(\"impossible\");\n  }\n\n  public static final FunctionDef empty =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.OPAQUE_SET_EMPTY;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          return OpaqueSet.empty();\n        }\n      };\n\n  public static final FunctionDef size =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.OPAQUE_SET_SIZE;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          OpaqueSet s = (OpaqueSet) args[0];\n          return I32.make(s.size());\n        }\n      };\n\n  public static final FunctionDef plus =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.OPAQUE_SET_PLUS;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          OpaqueSet s = (OpaqueSet) args[1];\n          return s.plus(args[0]);\n        }\n      };\n\n  public static final FunctionDef minus =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.OPAQUE_SET_MINUS;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          OpaqueSet s = (OpaqueSet) args[1];\n          return s.minus(args[0]);\n        }\n      };\n\n  public static final FunctionDef union =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.OPAQUE_SET_UNION;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          OpaqueSet s1 = (OpaqueSet) args[0];\n          OpaqueSet s2 = (OpaqueSet) args[1];\n          return s1.union(s2);\n        }\n      };\n\n  public static final FunctionDef diff =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.OPAQUE_SET_DIFF;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          OpaqueSet s1 = (OpaqueSet) args[0];\n          OpaqueSet s2 = (OpaqueSet) args[1];\n          return s1.diff(s2);\n        }\n      };\n\n  public static final FunctionDef choose =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.OPAQUE_SET_CHOOSE;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          OpaqueSet s = (OpaqueSet) args[0];\n          Pair<Term, OpaqueSet> p = s.choose();\n          if (p == null) {\n            return Constructors.none();\n          }\n          return Constructors.some(Constructors.tuple(p.fst(), p.snd()));\n        }\n      };\n\n  public static final FunctionDef member =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.OPAQUE_SET_MEMBER;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          OpaqueSet s = (OpaqueSet) args[1];\n          return BoolTerm.mk(s.member(args[0]));\n        }\n      };\n\n  public static final FunctionDef singleton =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.OPAQUE_SET_SINGLETON;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          return OpaqueSet.singleton(args[0]);\n        }\n      };\n\n  public static final FunctionDef subset =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.OPAQUE_SET_SUBSET;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          OpaqueSet s1 = (OpaqueSet) args[0];\n          OpaqueSet s2 = (OpaqueSet) args[1];\n          return BoolTerm.mk(s2.containsAll(s1));\n        }\n      };\n\n  public static final FunctionDef fromList =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.OPAQUE_SET_SUBSET;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          return OpaqueSet.fromCollection(Terms.termToTermList(args[0]));\n        }\n      };\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/functions/PredicateFunctionDef.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.functions;\n\nimport edu.harvard.seas.pl.formulog.ast.BindingType;\n\npublic interface PredicateFunctionDef extends FunctionDef {\n\n  int getIndex();\n\n  BindingType[] getBindingsForIndex();\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/functions/PrimitiveConversions.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.functions;\n\nimport edu.harvard.seas.pl.formulog.ast.Constructors;\nimport edu.harvard.seas.pl.formulog.ast.FP32;\nimport edu.harvard.seas.pl.formulog.ast.FP64;\nimport edu.harvard.seas.pl.formulog.ast.I32;\nimport edu.harvard.seas.pl.formulog.ast.I64;\nimport edu.harvard.seas.pl.formulog.ast.StringTerm;\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport edu.harvard.seas.pl.formulog.symbols.BuiltInFunctionSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.FunctionSymbol;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\npublic final class PrimitiveConversions {\n\n  private PrimitiveConversions() {\n    throw new AssertionError();\n  }\n\n  public static final FunctionDef i32ToI64 =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.i32ToI64;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          I32 x = (I32) args[0];\n          return I64.make(x.getVal());\n        }\n      };\n\n  public static final FunctionDef i32ToFp32 =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.i32ToFp32;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          I32 x = (I32) args[0];\n          return FP32.make(x.getVal());\n        }\n      };\n\n  public static final FunctionDef i32ToFp64 =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.i32ToFp64;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          I32 x = (I32) args[0];\n          return FP64.make(x.getVal());\n        }\n      };\n\n  public static final FunctionDef i64ToI32 =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.i64ToI32;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          I64 x = (I64) args[0];\n          return I32.make(x.getVal().intValue());\n        }\n      };\n\n  public static final FunctionDef i64ToFp32 =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.i64ToFp32;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          I64 x = (I64) args[0];\n          return FP32.make(x.getVal());\n        }\n      };\n\n  public static final FunctionDef i64ToFp64 =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.i64ToFp64;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          I64 x = (I64) args[0];\n          return FP64.make(x.getVal());\n        }\n      };\n\n  public static final FunctionDef fp32ToI32 =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.fp32ToI32;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          FP32 x = (FP32) args[0];\n          return I32.make(x.getVal().intValue());\n        }\n      };\n\n  public static final FunctionDef fp32ToI64 =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.fp32ToI64;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          FP32 x = (FP32) args[0];\n          return I64.make(x.getVal().longValue());\n        }\n      };\n\n  public static final FunctionDef fp32ToFp64 =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.fp32ToFp64;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          FP32 x = (FP32) args[0];\n          return FP64.make(x.getVal());\n        }\n      };\n\n  public static final FunctionDef fp64ToI32 =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.fp64ToI32;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          FP64 x = (FP64) args[0];\n          return I32.make(x.getVal().intValue());\n        }\n      };\n\n  public static final FunctionDef fp64ToI64 =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.fp64ToI64;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          FP64 x = (FP64) args[0];\n          return I64.make(x.getVal().longValue());\n        }\n      };\n\n  public static final FunctionDef fp64ToFp32 =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.fp64ToFp32;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          FP64 x = (FP64) args[0];\n          return FP32.make(x.getVal().floatValue());\n        }\n      };\n\n  private static final Pattern hex = Pattern.compile(\"0x([0-9a-fA-F]+)\");\n\n  public static final FunctionDef stringToI32 =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.stringToI32;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          try {\n            String s = ((StringTerm) args[0]).getVal();\n            Matcher m = hex.matcher(s);\n            Integer i;\n            if (m.matches()) {\n              i = Integer.parseUnsignedInt(m.group(1), 16);\n            } else {\n              i = Integer.parseInt(s);\n            }\n            return Constructors.some(I32.make(i));\n          } catch (NumberFormatException e) {\n            return Constructors.none();\n          }\n        }\n      };\n\n  public static final FunctionDef stringToI64 =\n      new FunctionDef() {\n\n        @Override\n        public FunctionSymbol getSymbol() {\n          return BuiltInFunctionSymbol.stringToI64;\n        }\n\n        @Override\n        public Term evaluate(Term[] args) throws EvaluationException {\n          try {\n            String s = ((StringTerm) args[0]).getVal();\n            Matcher m = hex.matcher(s);\n            Long i;\n            if (m.matches()) {\n              i = Long.parseUnsignedLong(m.group(1), 16);\n            } else {\n              i = Long.parseLong(s);\n            }\n            return Constructors.some(I64.make(i));\n          } catch (NumberFormatException e) {\n            return Constructors.none();\n          }\n        }\n      };\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/functions/RecordAccessor.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.functions;\n\nimport edu.harvard.seas.pl.formulog.ast.Constructor;\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport edu.harvard.seas.pl.formulog.symbols.FunctionSymbol;\n\npublic class RecordAccessor implements FunctionDef {\n\n  private final FunctionSymbol sym;\n  private final int index;\n\n  public RecordAccessor(FunctionSymbol sym, int index) {\n    this.sym = sym;\n    this.index = index;\n  }\n\n  @Override\n  public FunctionSymbol getSymbol() {\n    return sym;\n  }\n\n  public int getIndex() {\n    return index;\n  }\n\n  @Override\n  public Term evaluate(Term[] args) throws EvaluationException {\n    Constructor ctor = (Constructor) args[0];\n    return ctor.getArgs()[index];\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/functions/UserFunctionDef.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.functions;\n\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.ast.Var;\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport edu.harvard.seas.pl.formulog.symbols.FunctionSymbol;\nimport edu.harvard.seas.pl.formulog.unification.SimpleSubstitution;\nimport edu.harvard.seas.pl.formulog.unification.Substitution;\nimport java.util.List;\n\npublic class UserFunctionDef implements FunctionDef {\n\n  private final FunctionSymbol sym;\n  private final List<Var> params;\n  private Term body;\n\n  private UserFunctionDef(FunctionSymbol sym, List<Var> params, Term body) {\n    this.sym = sym;\n    this.params = params;\n    this.body = body;\n  }\n\n  public List<Var> getParams() {\n    return params;\n  }\n\n  public Term getBody() {\n    return body;\n  }\n\n  public void setBody(Term newBody) {\n    this.body = newBody;\n  }\n\n  @Override\n  public FunctionSymbol getSymbol() {\n    return sym;\n  }\n\n  @Override\n  public Term evaluate(Term[] args) throws EvaluationException {\n    Substitution s = new SimpleSubstitution();\n    assert params.size() == args.length;\n    int i = 0;\n    for (Var param : params) {\n      s.put(param, args[i]);\n      i++;\n    }\n    try {\n      return body.normalize(s);\n    } catch (EvaluationException e) {\n      throw new EvaluationException(\"Error evaluating function \" + sym + \":\\n\" + e.getMessage());\n    }\n  }\n\n  public static UserFunctionDef get(FunctionSymbol sym, List<Var> params, Term body) {\n    return new UserFunctionDef(sym, params, body);\n  }\n\n  @Override\n  public String toString() {\n    return \"UserFunctionDef [sym=\" + sym + \", params=\" + params + \", body=\" + body + \"]\";\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/magic/AdornedSymbol.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.magic;\n\nimport edu.harvard.seas.pl.formulog.symbols.RelationSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.WrappedRelationSymbol;\nimport edu.harvard.seas.pl.formulog.types.FunctorType;\nimport java.util.Arrays;\n\nclass AdornedSymbol implements WrappedRelationSymbol<RelationSymbol> {\n\n  private final RelationSymbol baseSymbol;\n  private final boolean[] adornment;\n\n  public AdornedSymbol(RelationSymbol baseSymbol, boolean[] adornment) {\n    assert adornment != null;\n    assert !(baseSymbol instanceof AdornedSymbol);\n    assert baseSymbol.isIdbSymbol();\n    this.baseSymbol = baseSymbol;\n    this.adornment = adornment;\n  }\n\n  public boolean[] getAdornment() {\n    return adornment;\n  }\n\n  @Override\n  public String toString() {\n    String s = getBaseSymbol() + \"_\";\n    for (boolean b : adornment) {\n      s += b ? \"b\" : \"f\";\n    }\n    return s;\n  }\n\n  @Override\n  public boolean isIdbSymbol() {\n    return baseSymbol.isIdbSymbol();\n  }\n\n  @Override\n  public boolean isBottomUp() {\n    return baseSymbol.isBottomUp();\n  }\n\n  @Override\n  public boolean isTopDown() {\n    return baseSymbol.isTopDown();\n  }\n\n  @Override\n  public FunctorType getCompileTimeType() {\n    return baseSymbol.getCompileTimeType();\n  }\n\n  @Override\n  public int getArity() {\n    return baseSymbol.getArity();\n  }\n\n  @Override\n  public RelationSymbol getBaseSymbol() {\n    return baseSymbol;\n  }\n\n  @Override\n  public boolean isDisk() {\n    return false;\n  }\n\n  @Override\n  public int hashCode() {\n    final int prime = 31;\n    int result = 1;\n    result = prime * result + Arrays.hashCode(adornment);\n    result = prime * result + ((baseSymbol == null) ? 0 : baseSymbol.hashCode());\n    return result;\n  }\n\n  @Override\n  public boolean equals(Object obj) {\n    if (this == obj) return true;\n    if (obj == null) return false;\n    if (getClass() != obj.getClass()) return false;\n    AdornedSymbol other = (AdornedSymbol) obj;\n    if (!Arrays.equals(adornment, other.adornment)) return false;\n    if (baseSymbol == null) {\n      if (other.baseSymbol != null) return false;\n    } else if (!baseSymbol.equals(other.baseSymbol)) return false;\n    return true;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/magic/Adornments.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.magic;\n\nimport edu.harvard.seas.pl.formulog.ast.BasicRule;\nimport edu.harvard.seas.pl.formulog.ast.ComplexLiteral;\nimport edu.harvard.seas.pl.formulog.ast.ComplexLiterals.ComplexLiteralVisitor;\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.ast.UnificationPredicate;\nimport edu.harvard.seas.pl.formulog.ast.UserPredicate;\nimport edu.harvard.seas.pl.formulog.ast.Var;\nimport edu.harvard.seas.pl.formulog.symbols.RelationSymbol;\nimport edu.harvard.seas.pl.formulog.unification.Unification;\nimport edu.harvard.seas.pl.formulog.validating.InvalidProgramException;\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\npublic final class Adornments {\n\n  private Adornments() {\n    throw new AssertionError();\n  }\n\n  public static UserPredicate adorn(UserPredicate a, Set<Var> boundVars, boolean topDownIsDefault) {\n    RelationSymbol origSym = a.getSymbol();\n    if (!topDownIsDefault && !origSym.isTopDown()) {\n      return a;\n    }\n    boolean defaultAdornment = !origSym.isBottomUp();\n    Term[] args = a.getArgs();\n    boolean[] adornment = new boolean[args.length];\n    for (int k = 0; k < args.length; k++) {\n      adornment[k] = defaultAdornment && boundVars.containsAll(args[k].varSet());\n    }\n    AdornedSymbol sym = new AdornedSymbol(origSym, adornment);\n    return UserPredicate.make(sym, args, a.isNegated());\n  }\n\n  public static BasicRule adornRule(\n      UserPredicate head, List<ComplexLiteral> body, boolean topDownIsDefault)\n      throws InvalidProgramException {\n    RelationSymbol sym = head.getSymbol();\n    boolean[] headAdornment;\n    if (sym instanceof AdornedSymbol) {\n      headAdornment = ((AdornedSymbol) head.getSymbol()).getAdornment();\n    } else {\n      headAdornment = new boolean[sym.getArity()];\n      for (int i = 0; i < headAdornment.length; ++i) {\n        headAdornment[i] = false;\n      }\n    }\n    Set<Var> boundVars = new HashSet<>();\n    Term[] headArgs = head.getArgs();\n    for (int i = 0; i < headArgs.length; i++) {\n      if (headAdornment[i]) {\n        boundVars.addAll(headArgs[i].varSet());\n      }\n    }\n    Map<Var, Integer> varCounts = BasicRule.make(head, body).countVariables();\n    List<ComplexLiteral> newBody = new ArrayList<>();\n    for (ComplexLiteral lit : body) {\n      ComplexLiteral newLit =\n          lit.accept(\n              new ComplexLiteralVisitor<Void, ComplexLiteral>() {\n\n                @Override\n                public ComplexLiteral visit(UnificationPredicate pred, Void input) {\n                  return pred;\n                }\n\n                @Override\n                public ComplexLiteral visit(UserPredicate pred, Void input) {\n                  if (pred.getSymbol().isIdbSymbol()) {\n                    pred = adorn(pred, boundVars, topDownIsDefault);\n                  }\n                  return pred;\n                }\n              },\n              null);\n      if (!Unification.canBindVars(newLit, boundVars, varCounts)) {\n        throw new InvalidProgramException(\n            \"Rule cannot be evaluated given the supplied order.\\n\"\n                + \"The problematic rule is:\\n\"\n                + BasicRule.make(head, body)\n                + \"\\nThe problematic literal is: \"\n                + lit);\n      }\n      boundVars.addAll(newLit.varSet());\n      newBody.add(newLit);\n    }\n    return BasicRule.make(head, newBody);\n    // List<ComplexLiteral> newBody = new ArrayList<>(body);\n    // for (int i = 0; i < newBody.size(); i++) {\n    // boolean ok = false;\n    // for (int j = i; j < newBody.size(); j++) {\n    // ComplexLiteral a = newBody.get(j);\n    // if (Unification.canBindVars(a, boundVars, varCounts)) {\n    // Collections.swap(newBody, i, j);\n    // int pos = i;\n    // a.accept(new ComplexLiteralVisitor<Void, Void>() {\n    //\n    // @Override\n    // public Void visit(UnificationPredicate unificationPredicate, Void input) {\n    // return null;\n    // }\n    //\n    // @Override\n    // public Void visit(UserPredicate userPredicate, Void input) {\n    // if (userPredicate.getSymbol().isIdbSymbol()) {\n    // newBody.set(pos, adorn(userPredicate, boundVars, topDownIsDefault));\n    // }\n    // return null;\n    // }\n    //\n    // }, null);\n    // boundVars.addAll(a.varSet());\n    // ok = true;\n    // break;\n    // }\n    // }\n    // if (!ok) {\n    // throw new InvalidProgramException(\n    // \"Cannot reorder rule to meet well-modeness restrictions: \" +\n    // BasicRule.make(head, body));\n    // }\n    // }\n    // return BasicRule.make(head, newBody);\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/magic/MagicSetTransformer.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.magic;\n\nimport edu.harvard.seas.pl.formulog.Configuration;\nimport edu.harvard.seas.pl.formulog.ast.BasicProgram;\nimport edu.harvard.seas.pl.formulog.ast.BasicRule;\nimport edu.harvard.seas.pl.formulog.ast.ComplexLiteral;\nimport edu.harvard.seas.pl.formulog.ast.ComplexLiterals.ComplexLiteralVisitor;\nimport edu.harvard.seas.pl.formulog.ast.Constructor;\nimport edu.harvard.seas.pl.formulog.ast.Expr;\nimport edu.harvard.seas.pl.formulog.ast.Exprs.ExprVisitor;\nimport edu.harvard.seas.pl.formulog.ast.Fold;\nimport edu.harvard.seas.pl.formulog.ast.FunctionCallFactory;\nimport edu.harvard.seas.pl.formulog.ast.FunctionCallFactory.FunctionCall;\nimport edu.harvard.seas.pl.formulog.ast.LetFunExpr;\nimport edu.harvard.seas.pl.formulog.ast.MatchClause;\nimport edu.harvard.seas.pl.formulog.ast.MatchExpr;\nimport edu.harvard.seas.pl.formulog.ast.Primitive;\nimport edu.harvard.seas.pl.formulog.ast.Program;\nimport edu.harvard.seas.pl.formulog.ast.Rule;\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.ast.Terms.TermVisitor;\nimport edu.harvard.seas.pl.formulog.ast.UnificationPredicate;\nimport edu.harvard.seas.pl.formulog.ast.UserPredicate;\nimport edu.harvard.seas.pl.formulog.ast.Var;\nimport edu.harvard.seas.pl.formulog.functions.FunctionDef;\nimport edu.harvard.seas.pl.formulog.functions.UserFunctionDef;\nimport edu.harvard.seas.pl.formulog.symbols.AbstractWrappedRelationSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.ConstructorSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.FunctionSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.PredicateFunctionSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.RelationSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.Symbol;\nimport edu.harvard.seas.pl.formulog.symbols.SymbolComparator;\nimport edu.harvard.seas.pl.formulog.symbols.SymbolManager;\nimport edu.harvard.seas.pl.formulog.symbols.TypeSymbol;\nimport edu.harvard.seas.pl.formulog.types.FunctorType;\nimport edu.harvard.seas.pl.formulog.util.DedupWorkList;\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport edu.harvard.seas.pl.formulog.validating.InvalidProgramException;\nimport edu.harvard.seas.pl.formulog.validating.Stratifier;\nimport edu.harvard.seas.pl.formulog.validating.Stratum;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\npublic class MagicSetTransformer {\n\n  private final Program<UserPredicate, BasicRule> origProg;\n  private boolean topDownIsDefault;\n\n  private static final boolean debug = Configuration.debugMst;\n\n  public MagicSetTransformer(Program<UserPredicate, BasicRule> prog) {\n    this.origProg = prog;\n  }\n\n  public BasicProgram transform(boolean useDemandTransformation, boolean restoreStratification)\n      throws InvalidProgramException {\n    BasicProgram prog;\n    if (origProg.hasQuery()) {\n      prog = transformForQuery(origProg.getQuery(), useDemandTransformation, restoreStratification);\n    } else {\n      prog = transformNoQuery(useDemandTransformation, restoreStratification);\n    }\n    if (debug) {\n      System.err.println(\"Rewritten rules...\");\n      List<RelationSymbol> syms =\n          prog.getRuleSymbols().stream()\n              .sorted(SymbolComparator.INSTANCE)\n              .collect(Collectors.toList());\n      for (RelationSymbol sym : syms) {\n        for (BasicRule r : prog.getRules(sym)) {\n          System.err.println(r);\n        }\n      }\n    }\n    return prog;\n  }\n\n  private BasicProgram transformForQuery(\n      UserPredicate query, boolean useDemandTransformation, boolean restoreStratification)\n      throws InvalidProgramException {\n    topDownIsDefault = true;\n    if (query.isNegated()) {\n      throw new InvalidProgramException(\"Query cannot be negated\");\n    }\n    BasicProgram newProg;\n    if (query.getSymbol().isEdbSymbol()) {\n      newProg = makeEdbProgram(query);\n    } else {\n      UserPredicate adornedQuery =\n          Adornments.adorn(query, Collections.emptySet(), topDownIsDefault);\n      Set<Pair<BasicRule, Integer>> adRules =\n          adorn(Collections.singleton(adornedQuery.getSymbol()));\n      Set<BasicRule> magicRules = makeMagicRules(adRules);\n      magicRules.add(makeSeedRule(adornedQuery));\n      BasicRule queryRule = makeQueryRule(adornedQuery);\n      UserPredicate newQuery = queryRule.getHead();\n      BasicProgram magicProg = new ProgramImpl(magicRules, newQuery);\n      if (restoreStratification && !isStratified(magicProg)) {\n        magicProg = stratify(magicProg, Pair.map(adRules, (rule, num) -> rule));\n      }\n      ((ProgramImpl) magicProg).rules.put(newQuery.getSymbol(), Collections.singleton(queryRule));\n      if (useDemandTransformation) {\n        magicProg = applyDemandTransformation(magicProg, restoreStratification);\n      }\n      newProg = magicProg;\n    }\n    return newProg;\n  }\n\n  private BasicRule makeSeedRule(UserPredicate adornedQuery) {\n    return BasicRule.make(createInputAtom(adornedQuery));\n  }\n\n  private BasicRule makeQueryRule(UserPredicate query) {\n    RelationSymbol oldSym = query.getSymbol();\n    RelationSymbol querySym =\n        new RelationSymbol() {\n\n          @Override\n          public FunctorType getCompileTimeType() {\n            return oldSym.getCompileTimeType();\n          }\n\n          @Override\n          public int getArity() {\n            return oldSym.getArity();\n          }\n\n          @Override\n          public boolean isIdbSymbol() {\n            return true;\n          }\n\n          @Override\n          public boolean isBottomUp() {\n            return true;\n          }\n\n          @Override\n          public boolean isTopDown() {\n            return false;\n          }\n\n          @Override\n          public boolean isDisk() {\n            return false;\n          }\n\n          @Override\n          public String toString() {\n            Symbol sym = oldSym;\n            if (oldSym instanceof AdornedSymbol) {\n              sym = ((AdornedSymbol) oldSym).getBaseSymbol();\n            }\n            return \"query:\" + sym;\n          }\n        };\n    Term[] args = query.getArgs();\n    Term[] newArgs = new Term[args.length];\n    Term[] hdArgs = new Term[args.length];\n    List<ComplexLiteral> body = new ArrayList<>();\n    Set<Var> seen = new HashSet<>();\n    for (int i = 0; i < args.length; ++i) {\n      Term t = args[i];\n      if (!(t instanceof Var) || !seen.add((Var) t)) {\n        Var x = Var.fresh();\n        body.add(UnificationPredicate.make(x, t, false));\n        t = x;\n      }\n      newArgs[i] = hdArgs[i] = t;\n    }\n    body.add(0, UserPredicate.make(oldSym, newArgs, query.isNegated()));\n    UserPredicate hd = UserPredicate.make(querySym, hdArgs, query.isNegated());\n    return BasicRule.make(hd, body);\n  }\n\n  private BasicProgram makeEdbProgram(UserPredicate query) {\n    BasicRule queryRule = makeQueryRule(query);\n    RelationSymbol oldQuerySym = query.getSymbol();\n    return new BasicProgram() {\n\n      @Override\n      public Set<FunctionSymbol> getFunctionSymbols() {\n        return Collections.emptySet();\n      }\n\n      @Override\n      public Set<RelationSymbol> getFactSymbols() {\n        return Collections.singleton(oldQuerySym);\n      }\n\n      @Override\n      public Set<RelationSymbol> getRuleSymbols() {\n        return Collections.singleton(getQuery().getSymbol());\n      }\n\n      @Override\n      public FunctionDef getDef(FunctionSymbol sym) {\n        throw new IllegalArgumentException(\"No definition for function with symbol: \" + sym);\n      }\n\n      @Override\n      public Set<Term[]> getFacts(RelationSymbol sym) {\n        if (oldQuerySym.equals(sym)) {\n          return origProg.getFacts(oldQuerySym);\n        }\n        return Collections.emptySet();\n      }\n\n      @Override\n      public Set<BasicRule> getRules(RelationSymbol sym) {\n        if (!sym.equals(getQuery().getSymbol())) {\n          return Collections.emptySet();\n        }\n        return Collections.singleton(queryRule);\n      }\n\n      @Override\n      public SymbolManager getSymbolManager() {\n        return origProg.getSymbolManager();\n      }\n\n      @Override\n      public boolean hasQuery() {\n        return true;\n      }\n\n      @Override\n      public UserPredicate getQuery() {\n        return queryRule.getHead();\n      }\n\n      @Override\n      public FunctionCallFactory getFunctionCallFactory() {\n        return origProg.getFunctionCallFactory();\n      }\n\n      @Override\n      public Set<ConstructorSymbol> getUninterpretedFunctionSymbols() {\n        return origProg.getUninterpretedFunctionSymbols();\n      }\n\n      @Override\n      public Set<TypeSymbol> getTypeSymbols() {\n        return origProg.getTypeSymbols();\n      }\n    };\n  }\n\n  private BasicProgram transformNoQuery(\n      boolean useDemandTransformation, boolean restoreStratification)\n      throws InvalidProgramException {\n    topDownIsDefault = false;\n    Set<RelationSymbol> bottomUpSymbols = new HashSet<>();\n    for (RelationSymbol sym : origProg.getRuleSymbols()) {\n      if (!sym.isTopDown()) {\n        bottomUpSymbols.add(sym);\n      }\n    }\n    Set<Pair<BasicRule, Integer>> adRules = adorn(bottomUpSymbols);\n    Set<BasicRule> magicRules = makeMagicRules(adRules);\n    BasicProgram magicProg = new ProgramImpl(magicRules, null);\n    if (restoreStratification && !isStratified(magicProg)) {\n      magicProg = stratify(magicProg, Pair.map(adRules, (rule, num) -> rule));\n    }\n    if (useDemandTransformation) {\n      magicProg = applyDemandTransformation(magicProg, restoreStratification);\n    }\n    return magicProg;\n  }\n\n  private BasicProgram applyDemandTransformation(BasicProgram prog, boolean mustBeStratified)\n      throws InvalidProgramException {\n    BasicProgram prog2 = stripAdornments(prog);\n    if (isStratified(prog2)) {\n      return prog2;\n    }\n    return prog;\n  }\n\n  private BasicProgram stripAdornments(BasicProgram prog) throws InvalidProgramException {\n    Set<BasicRule> rules = new HashSet<>();\n    for (RelationSymbol sym : prog.getRuleSymbols()) {\n      for (BasicRule r : prog.getRules(sym)) {\n        UserPredicate newHead = stripAdornment(r.getHead());\n        List<ComplexLiteral> newBody = new ArrayList<>();\n        for (ComplexLiteral atom : r) {\n          newBody.add(stripAdornment(atom));\n        }\n        rules.add(BasicRule.make(newHead, newBody));\n      }\n    }\n    UserPredicate query = null;\n    if (prog.hasQuery()) {\n      query = stripAdornment(prog.getQuery());\n    }\n    return new ProgramImpl(rules, query);\n  }\n\n  private static <C extends ComplexLiteral> C stripAdornment(C atom) {\n    return atom.accept(\n        new ComplexLiteralVisitor<Void, C>() {\n\n          @SuppressWarnings(\"unchecked\")\n          @Override\n          public C visit(UnificationPredicate unificationPredicate, Void input) {\n            return (C) unificationPredicate;\n          }\n\n          @SuppressWarnings(\"unchecked\")\n          @Override\n          public C visit(UserPredicate userPredicate, Void input) {\n            RelationSymbol sym = userPredicate.getSymbol();\n            if (sym instanceof PositiveSymbol) {\n              sym = ((PositiveSymbol) sym).getBaseSymbol();\n              if (sym instanceof AdornedSymbol) {\n                sym = ((AdornedSymbol) sym).getBaseSymbol();\n              }\n              sym = new PositiveSymbol(sym);\n            } else if (sym instanceof AdornedSymbol) {\n              sym = ((AdornedSymbol) sym).getBaseSymbol();\n            }\n            return (C) UserPredicate.make(sym, userPredicate.getArgs(), userPredicate.isNegated());\n          }\n        },\n        null);\n  }\n\n  private Set<Pair<BasicRule, Integer>> adorn(Set<RelationSymbol> seeds)\n      throws InvalidProgramException {\n    if (debug) {\n      System.err.println(\"Adorning rules...\");\n    }\n    Set<Pair<BasicRule, Integer>> adRules = new HashSet<>();\n    DedupWorkList<RelationSymbol> worklist = new DedupWorkList<>();\n    for (RelationSymbol seed : seeds) {\n      worklist.push(seed);\n    }\n    HiddenPredicateFinder hpf = new HiddenPredicateFinder(origProg);\n    int ruleNum = 0;\n    while (!worklist.isEmpty()) {\n      RelationSymbol adSym = worklist.pop();\n      RelationSymbol origSym = adSym;\n      if (adSym instanceof AdornedSymbol) {\n        origSym = ((AdornedSymbol) adSym).getBaseSymbol();\n      }\n      for (BasicRule r : origProg.getRules(origSym)) {\n        for (RelationSymbol sym : hpf.visit(r)) {\n          if (exploreTopDown(sym)) {\n            throw new InvalidProgramException(\n                \"Cannot refer to top-down IDB predicate \"\n                    + sym\n                    + \" in a function; consider annotating \"\n                    + sym\n                    + \" with @bottomup\");\n          }\n          if (sym.isIdbSymbol()) {\n            worklist.push(sym);\n          }\n        }\n        UserPredicate head = r.getHead();\n        UserPredicate adHead = UserPredicate.make(adSym, head.getArgs(), head.isNegated());\n        BasicRule adRule = Adornments.adornRule(adHead, Util.iterableToList(r), topDownIsDefault);\n        for (ComplexLiteral a : adRule) {\n          a.accept(\n              new ComplexLiteralVisitor<Void, Void>() {\n\n                @Override\n                public Void visit(UnificationPredicate unificationPredicate, Void input) {\n                  // Do nothing\n                  return null;\n                }\n\n                @Override\n                public Void visit(UserPredicate userPredicate, Void input) {\n                  RelationSymbol sym = userPredicate.getSymbol();\n                  if (sym.isIdbSymbol()) {\n                    worklist.push(sym);\n                  }\n                  return null;\n                }\n              },\n              null);\n        }\n        if (debug) {\n          System.err.println(\"--\" + ruleNum + \"--\\n\" + adRule);\n        }\n        adRules.add(new Pair<>(adRule, ruleNum));\n        ruleNum++;\n      }\n    }\n    return adRules;\n  }\n\n  private Set<BasicRule> makeMagicRules(Set<Pair<BasicRule, Integer>> adornedRules) {\n    Set<BasicRule> magicRules = new HashSet<>();\n    for (Pair<BasicRule, Integer> p : adornedRules) {\n      magicRules.addAll(makeMagicRules(p.fst(), p.snd()));\n    }\n    return magicRules;\n  }\n\n  private boolean exploreTopDown(RelationSymbol sym) {\n    if (sym instanceof AdornedSymbol) {\n      sym = ((AdornedSymbol) sym).getBaseSymbol();\n    }\n    if (!sym.isIdbSymbol()) {\n      return false;\n    }\n    return sym.isTopDown() || (topDownIsDefault && !sym.isBottomUp());\n  }\n\n  private Set<BasicRule> makeMagicRules(BasicRule r, int number) {\n    int[] supCount = {0};\n    Set<BasicRule> magicRules = new HashSet<>();\n    List<Set<Var>> liveVarsByAtom = liveVarsByAtom(r);\n    List<ComplexLiteral> l = new ArrayList<>();\n    UserPredicate head = r.getHead();\n    Set<Var> curLiveVars = new HashSet<>();\n    if (exploreTopDown(head.getSymbol())) {\n      UserPredicate inputAtom = createInputAtom(head);\n      l.add(inputAtom);\n      curLiveVars.addAll(inputAtom.varSet());\n    }\n    int i = 0;\n    for (ComplexLiteral a : r) {\n      Set<Var> futureLiveVars = liveVarsByAtom.get(i);\n      Set<Var> nextLiveVars =\n          curLiveVars.stream().filter(futureLiveVars::contains).collect(Collectors.toSet());\n      l =\n          a.accept(\n              new ComplexLiteralVisitor<List<ComplexLiteral>, List<ComplexLiteral>>() {\n\n                @Override\n                public List<ComplexLiteral> visit(\n                    UnificationPredicate unificationPredicate, List<ComplexLiteral> l) {\n                  l.add(a);\n                  return l;\n                }\n\n                @Override\n                public List<ComplexLiteral> visit(\n                    UserPredicate userPredicate, List<ComplexLiteral> l) {\n                  RelationSymbol sym = userPredicate.getSymbol();\n                  if (exploreTopDown(sym)) {\n                    Set<Var> supVars =\n                        a.varSet().stream()\n                            .filter(curLiveVars::contains)\n                            .collect(Collectors.toSet());\n                    supVars.addAll(nextLiveVars);\n                    UserPredicate supAtom =\n                        createSupAtom(supVars, number, supCount[0], head.getSymbol());\n                    magicRules.add(BasicRule.make(supAtom, l));\n                    magicRules.add(\n                        BasicRule.make(\n                            createInputAtom(userPredicate), Collections.singletonList(supAtom)));\n                    l = new ArrayList<>();\n                    l.add(supAtom);\n                    l.add(a);\n                    supCount[0]++;\n                  } else {\n                    l.add(a);\n                  }\n                  return l;\n                }\n              },\n              l);\n      curLiveVars.clear();\n      curLiveVars.addAll(nextLiveVars);\n      for (Var v : a.varSet()) {\n        if (futureLiveVars.contains(v)) {\n          curLiveVars.add(v);\n        }\n      }\n      i++;\n    }\n    magicRules.add(BasicRule.make(head, l));\n    return magicRules;\n  }\n\n  private List<Set<Var>> liveVarsByAtom(BasicRule r) {\n    List<Set<Var>> liveVars = new ArrayList<>();\n    Set<Var> acc = r.getHead().varSet();\n    liveVars.add(acc);\n    for (int i = r.getBodySize() - 1; i > 0; i--) {\n      acc = new HashSet<>(acc);\n      acc.addAll(r.getBody(i).varSet());\n      liveVars.add(acc);\n    }\n    Collections.reverse(liveVars);\n    return liveVars;\n  }\n\n  private UserPredicate createSupAtom(\n      Set<Var> curLiveVars, int ruleNum, int supCount, Symbol headSym) {\n    List<Term> l = new ArrayList<>(curLiveVars);\n    l.sort(\n        new Comparator<Term>() {\n\n          @Override\n          public int compare(Term o1, Term o2) {\n            return o1.toString().compareTo(o2.toString());\n          }\n        });\n    Term[] args = l.toArray(new Term[0]);\n    SupSymbol supSym = new SupSymbol(ruleNum, supCount, args.length);\n    return UserPredicate.make(supSym, args, false);\n  }\n\n  private UserPredicate createInputAtom(UserPredicate a) {\n    AdornedSymbol headSym = (AdornedSymbol) a.getSymbol();\n    InputSymbol inputSym = new InputSymbol(headSym);\n    Term[] inputArgs = new Term[inputSym.getArity()];\n    Term[] args = a.getArgs();\n    boolean[] adornment = headSym.getAdornment();\n    for (int i = 0, j = 0; i < args.length; i++) {\n      if (adornment[i]) {\n        inputArgs[j] = args[i];\n        j++;\n      }\n    }\n    return UserPredicate.make(inputSym, inputArgs, false);\n  }\n\n  public static class SupSymbol implements RelationSymbol {\n\n    private final int ruleNum;\n    private final int supCount;\n    private final int arity;\n\n    public SupSymbol(int ruleNum, int supCount, int arity) {\n      this.ruleNum = ruleNum;\n      this.supCount = supCount;\n      this.arity = arity;\n    }\n\n    @Override\n    public int getArity() {\n      return arity;\n    }\n\n    @Override\n    public int hashCode() {\n      final int prime = 31;\n      int result = 1;\n      result = prime * result + arity;\n      result = prime * result + ruleNum;\n      result = prime * result + supCount;\n      return result;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n      if (this == obj) return true;\n      if (obj == null) return false;\n      if (getClass() != obj.getClass()) return false;\n      SupSymbol other = (SupSymbol) obj;\n      if (arity != other.arity) return false;\n      if (ruleNum != other.ruleNum) return false;\n      if (supCount != other.supCount) return false;\n      return true;\n    }\n\n    @Override\n    public String toString() {\n      return \"sup_\" + ruleNum + \"_\" + supCount;\n    }\n\n    @Override\n    public FunctorType getCompileTimeType() {\n      throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public boolean isIdbSymbol() {\n      return true;\n    }\n\n    @Override\n    public boolean isDisk() {\n      return false;\n    }\n\n    @Override\n    public boolean isBottomUp() {\n      throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public boolean isTopDown() {\n      throw new UnsupportedOperationException();\n    }\n  }\n\n  public static class InputSymbol extends AbstractWrappedRelationSymbol<AdornedSymbol> {\n\n    private final int arity;\n\n    public InputSymbol(AdornedSymbol baseSymbol) {\n      super(baseSymbol);\n      int nbound = 0;\n      for (boolean b : getBaseSymbol().getAdornment()) {\n        nbound += b ? 1 : 0;\n      }\n      arity = nbound;\n    }\n\n    @Override\n    public int getArity() {\n      return arity;\n    }\n\n    @Override\n    public String toString() {\n      return \"input_\" + getBaseSymbol();\n    }\n\n    @Override\n    public FunctorType getCompileTimeType() {\n      throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public boolean isBottomUp() {\n      throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public boolean isTopDown() {\n      throw new UnsupportedOperationException();\n    }\n  }\n\n  private boolean isStratified(BasicProgram p) {\n    try {\n      Stratifier stratifier = new Stratifier(p);\n      for (Stratum s : stratifier.stratify()) {\n        if (s.hasRecursiveNegationOrAggregation()) {\n          return false;\n        }\n      }\n      return true;\n    } catch (InvalidProgramException e) {\n      return false;\n    }\n  }\n\n  private Set<BasicRule> adjustAdornedRules(Collection<BasicRule> adRules) {\n    Set<BasicRule> newRules = new HashSet<>();\n    for (BasicRule r : adRules) {\n      UserPredicate head = r.getHead();\n      if (exploreTopDown(head.getSymbol())) {\n        List<ComplexLiteral> body = new ArrayList<>();\n        body.add(createInputAtom(head));\n        r.forEach(body::add);\n        newRules.add(BasicRule.make(head, body));\n      } else {\n        newRules.add(r);\n      }\n    }\n    return newRules;\n  }\n\n  private ProgramImpl stratify(BasicProgram p, Set<BasicRule> adornedRules)\n      throws InvalidProgramException {\n    Set<BasicRule> newRules = new HashSet<>();\n    for (RelationSymbol sym : p.getRuleSymbols()) {\n      for (BasicRule r : p.getRules(sym)) {\n        UserPredicate head = makePositive(r.getHead());\n        List<ComplexLiteral> body = makePositive(r);\n        newRules.add(BasicRule.make(head, body));\n      }\n    }\n    newRules.addAll(adjustAdornedRules(adornedRules));\n    return new ProgramImpl(newRules, p.getQuery());\n  }\n\n  private <C extends ComplexLiteral> C makePositive(C atom) {\n    return atom.accept(\n        new ComplexLiteralVisitor<Void, C>() {\n\n          @SuppressWarnings(\"unchecked\")\n          @Override\n          public C visit(UnificationPredicate unificationPredicate, Void input) {\n            return (C) unificationPredicate;\n          }\n\n          @SuppressWarnings(\"unchecked\")\n          @Override\n          public C visit(UserPredicate userPredicate, Void input) {\n            RelationSymbol sym = userPredicate.getSymbol();\n            if (sym.isIdbSymbol() && !(sym instanceof InputSymbol || sym instanceof SupSymbol)) {\n              if (userPredicate.isNegated()) {\n                return null;\n              }\n              userPredicate =\n                  UserPredicate.make(new PositiveSymbol(sym), userPredicate.getArgs(), false);\n            }\n            return (C) userPredicate;\n          }\n        },\n        null);\n  }\n\n  private List<ComplexLiteral> makePositive(Iterable<ComplexLiteral> atoms) {\n    List<ComplexLiteral> l = new ArrayList<>();\n    for (ComplexLiteral a : atoms) {\n      ComplexLiteral b = makePositive(a);\n      if (b != null) {\n        l.add(b);\n      }\n    }\n    return l;\n  }\n\n  private static class PositiveSymbol extends AbstractWrappedRelationSymbol<RelationSymbol> {\n\n    public PositiveSymbol(RelationSymbol baseSymbol) {\n      super(baseSymbol);\n    }\n\n    @Override\n    public String toString() {\n      return \"p_\" + getBaseSymbol();\n    }\n\n    @Override\n    public FunctorType getCompileTimeType() {\n      throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public boolean isBottomUp() {\n      throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public boolean isTopDown() {\n      throw new UnsupportedOperationException();\n    }\n  }\n\n  private static class HiddenPredicateFinder {\n\n    private final Program<UserPredicate, BasicRule> origProg;\n    private final Set<FunctionSymbol> visitedFunctions = new HashSet<>();\n    private final Set<RelationSymbol> seenPredicates = new HashSet<>();\n\n    public HiddenPredicateFinder(Program<UserPredicate, BasicRule> origProg) {\n      this.origProg = origProg;\n    }\n\n    private void findHiddenPredicates(Term t, Set<RelationSymbol> s) {\n      t.accept(predicatesInTermExtractor, s);\n    }\n\n    public Set<RelationSymbol> allSeenPredicates() {\n      return seenPredicates;\n    }\n\n    public Set<RelationSymbol> visit(UserFunctionDef def) {\n      Set<RelationSymbol> s = new HashSet<>();\n      if (visitedFunctions.add(def.getSymbol())) {\n        findHiddenPredicates(def.getBody(), s);\n      }\n      return s;\n    }\n\n    private void visit(ComplexLiteral l, Set<RelationSymbol> s) {\n      l.accept(\n          new ComplexLiteralVisitor<Void, Void>() {\n\n            @Override\n            public Void visit(UnificationPredicate unificationPredicate, Void input) {\n              findHiddenPredicates(unificationPredicate.getLhs(), s);\n              findHiddenPredicates(unificationPredicate.getRhs(), s);\n              return null;\n            }\n\n            @Override\n            public Void visit(UserPredicate userPredicate, Void input) {\n              for (Term t : userPredicate.getArgs()) {\n                findHiddenPredicates(t, s);\n              }\n              return null;\n            }\n          },\n          null);\n    }\n\n    public Set<RelationSymbol> visit(\n        Rule<? extends ComplexLiteral, ? extends ComplexLiteral> rule) {\n      Set<RelationSymbol> s = new HashSet<>();\n      visit(rule.getHead(), s);\n      for (ComplexLiteral l : rule) {\n        visit(l, s);\n      }\n      return s;\n    }\n\n    private TermVisitor<Set<RelationSymbol>, Void> predicatesInTermExtractor =\n        new TermVisitor<Set<RelationSymbol>, Void>() {\n\n          @Override\n          public Void visit(Var t, Set<RelationSymbol> in) {\n            return null;\n          }\n\n          @Override\n          public Void visit(Constructor c, Set<RelationSymbol> in) {\n            for (Term t : c.getArgs()) {\n              t.accept(this, in);\n            }\n            return null;\n          }\n\n          @Override\n          public Void visit(Primitive<?> p, Set<RelationSymbol> in) {\n            return null;\n          }\n\n          @Override\n          public Void visit(Expr e, Set<RelationSymbol> in) {\n            e.accept(predicatesInExprExtractor, in);\n            return null;\n          }\n        };\n\n    private ExprVisitor<Set<RelationSymbol>, Void> predicatesInExprExtractor =\n        new ExprVisitor<Set<RelationSymbol>, Void>() {\n\n          @Override\n          public Void visit(MatchExpr matchExpr, Set<RelationSymbol> in) {\n            matchExpr.getMatchee().accept(predicatesInTermExtractor, in);\n            for (MatchClause cl : matchExpr) {\n              cl.getRhs().accept(predicatesInTermExtractor, in);\n            }\n            return null;\n          }\n\n          @Override\n          public Void visit(FunctionCall funcCall, Set<RelationSymbol> in) {\n            FunctionSymbol sym = funcCall.getSymbol();\n            if (visitedFunctions.add(sym)) {\n              if (sym instanceof PredicateFunctionSymbol) {\n                RelationSymbol psym = ((PredicateFunctionSymbol) sym).getPredicateSymbol();\n                seenPredicates.add(psym);\n                in.add(psym);\n              }\n              FunctionDef def = origProg.getDef(sym);\n              if (def instanceof UserFunctionDef) {\n                ((UserFunctionDef) def).getBody().accept(predicatesInTermExtractor, in);\n              }\n            }\n            for (Term t : funcCall.getArgs()) {\n              t.accept(predicatesInTermExtractor, in);\n            }\n            return null;\n          }\n\n          @Override\n          public Void visit(LetFunExpr funcDef, Set<RelationSymbol> in) {\n            throw new AssertionError(\"impossible\");\n          }\n\n          @Override\n          public Void visit(Fold fold, Set<RelationSymbol> in) {\n            fold.getShamCall().accept(this, in);\n            return null;\n          }\n        };\n  }\n\n  private class ProgramImpl implements BasicProgram {\n\n    private final Map<RelationSymbol, Set<BasicRule>> rules = new HashMap<>();\n    private final Map<RelationSymbol, Set<Term[]>> facts = new HashMap<>();\n    private final UserPredicate query;\n\n    public ProgramImpl(Set<BasicRule> rs, UserPredicate query) throws InvalidProgramException {\n      SymbolManager sm = origProg.getSymbolManager();\n      HiddenPredicateFinder hpf = new HiddenPredicateFinder(origProg);\n      for (BasicRule r : rs) {\n        RelationSymbol headSym = r.getHead().getSymbol();\n        Util.lookupOrCreate(rules, headSym, HashSet::new).add(r);\n        sm.registerSymbol(headSym);\n        for (ComplexLiteral l : r) {\n          if (l instanceof UserPredicate) {\n            RelationSymbol sym = ((UserPredicate) l).getSymbol();\n            sm.registerSymbol(sym);\n            if (sym.isEdbSymbol()) {\n              facts.putIfAbsent(sym, origProg.getFacts(sym));\n            } else {\n              rules.putIfAbsent(sym, new HashSet<>());\n            }\n          }\n        }\n        hpf.visit(r);\n      }\n      for (FunctionSymbol sym : origProg.getFunctionSymbols()) {\n        FunctionDef def = origProg.getDef(sym);\n        if (def instanceof UserFunctionDef) {\n          hpf.visit((UserFunctionDef) def);\n        }\n      }\n      for (RelationSymbol psym : hpf.allSeenPredicates()) {\n        if (exploreTopDown(psym)) {\n          throw new InvalidProgramException(\n              \"Cannot refer to top-down IDB predicate \"\n                  + psym\n                  + \" in a function; consider annotating \"\n                  + psym\n                  + \" with @bottomup\");\n        }\n        if (psym.isEdbSymbol()) {\n          facts.putIfAbsent(psym, origProg.getFacts(psym));\n        }\n        if (psym.isIdbSymbol()) {\n          rules.putIfAbsent(psym, new HashSet<>());\n        }\n      }\n      // Do not keep unnecessary facts around if there is a query.\n      if (query == null) {\n        for (RelationSymbol sym : origProg.getFactSymbols()) {\n          facts.putIfAbsent(sym, origProg.getFacts(sym));\n        }\n      }\n      this.query = query;\n    }\n\n    @Override\n    public Set<FunctionSymbol> getFunctionSymbols() {\n      return origProg.getFunctionSymbols();\n    }\n\n    @Override\n    public Set<RelationSymbol> getFactSymbols() {\n      return Collections.unmodifiableSet(facts.keySet());\n    }\n\n    @Override\n    public Set<RelationSymbol> getRuleSymbols() {\n      return Collections.unmodifiableSet(rules.keySet());\n    }\n\n    @Override\n    public FunctionDef getDef(FunctionSymbol sym) {\n      return origProg.getDef(sym);\n    }\n\n    @Override\n    public Set<Term[]> getFacts(RelationSymbol sym) {\n      assert sym.isEdbSymbol();\n      return Util.lookupOrCreate(facts, sym, () -> Collections.emptySet());\n    }\n\n    @Override\n    public Set<BasicRule> getRules(RelationSymbol sym) {\n      assert sym.isIdbSymbol();\n      return Util.lookupOrCreate(rules, sym, () -> Collections.emptySet());\n    }\n\n    @Override\n    public SymbolManager getSymbolManager() {\n      return origProg.getSymbolManager();\n    }\n\n    @Override\n    public boolean hasQuery() {\n      return query != null;\n    }\n\n    @Override\n    public UserPredicate getQuery() {\n      return query;\n    }\n\n    @Override\n    public FunctionCallFactory getFunctionCallFactory() {\n      return origProg.getFunctionCallFactory();\n    }\n\n    @Override\n    public Set<ConstructorSymbol> getUninterpretedFunctionSymbols() {\n      return origProg.getUninterpretedFunctionSymbols();\n    }\n\n    @Override\n    public Set<TypeSymbol> getTypeSymbols() {\n      return origProg.getTypeSymbols();\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/parsing/FactFileParser.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.parsing;\n\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.TabSeparatedTermLineContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.TsvFileContext;\nimport java.util.Set;\n\nclass FactFileParser {\n\n  private final ParsingContext pc;\n\n  public FactFileParser(ParsingContext parsingContext) {\n    pc = parsingContext;\n  }\n\n  public void loadFacts(TsvFileContext ctx, int expectedArity, Set<Term[]> acc)\n      throws ParseException {\n    TermExtractor termExtractor = new TermExtractor(pc);\n    VariableCheckPass varChecker = new VariableCheckPass(pc.symbolManager());\n    for (TabSeparatedTermLineContext l : ctx.tabSeparatedTermLine()) {\n      Term[] args = termExtractor.extractArray(l.term());\n      if (args.length != expectedArity) {\n        throw new ParseException(\n            l.start.getLine(),\n            \"Arity mismatch: expected \" + expectedArity + \" terms, but got \" + args.length);\n      }\n      try {\n        args = varChecker.checkFact(args);\n      } catch (VariableCheckPassException e) {\n        throw new ParseException(l.start.getLine(), e.getMessage());\n      }\n      acc.add(args);\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/parsing/Identifier.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.parsing;\n\nimport edu.harvard.seas.pl.formulog.ast.Var;\nimport edu.harvard.seas.pl.formulog.symbols.FunctionSymbol;\n\npublic class Identifier {\n\n  private final Object val;\n\n  private Identifier(Object val) {\n    this.val = val;\n  }\n\n  public static Identifier make(Var var) {\n    return new Identifier(var);\n  }\n\n  public static Identifier make(FunctionSymbol sym) {\n    return new Identifier(sym);\n  }\n\n  public boolean isFunctionSymbol() {\n    return val instanceof FunctionSymbol;\n  }\n\n  public boolean isVar() {\n    return val instanceof Var;\n  }\n\n  public FunctionSymbol asFunctionSymbol() {\n    return (FunctionSymbol) val;\n  }\n\n  public Var asVar() {\n    return (Var) val;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/parsing/ParseException.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.parsing;\n\npublic class ParseException extends Exception {\n\n  private static final long serialVersionUID = 47248799671953000L;\n  private final String fileName;\n  private final int lineNo;\n\n  /** Constructs an exception signifying a parsing error. */\n  public ParseException(String fileName, int lineNo, String message) {\n    super(message);\n    this.fileName = fileName;\n    this.lineNo = lineNo;\n  }\n\n  /** Constructs an exception signifying a parsing error. */\n  public ParseException(int lineNo, String message) {\n    this(null, lineNo, message);\n  }\n\n  public ParseException(UncheckedParseException e) {\n    super(e.getMessage());\n    this.fileName = e.getFileName();\n    this.lineNo = e.getLineNo();\n  }\n\n  public String getFileName() {\n    return fileName;\n  }\n\n  public int getLineNo() {\n    return lineNo;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/parsing/Parser.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.parsing;\n\nimport edu.harvard.seas.pl.formulog.Main;\nimport edu.harvard.seas.pl.formulog.ast.BasicProgram;\nimport edu.harvard.seas.pl.formulog.ast.BasicRule;\nimport edu.harvard.seas.pl.formulog.ast.Program;\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.ast.UserPredicate;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogLexer;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.ProgContext;\nimport edu.harvard.seas.pl.formulog.symbols.RelationSymbol;\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport java.io.FileNotFoundException;\nimport java.io.FileReader;\nimport java.io.IOException;\nimport java.io.Reader;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.Future;\nimport org.antlr.v4.runtime.BufferedTokenStream;\nimport org.antlr.v4.runtime.CharStream;\nimport org.antlr.v4.runtime.CharStreams;\nimport org.antlr.v4.runtime.CommonTokenStream;\nimport org.antlr.v4.runtime.TokenStream;\nimport org.antlr.v4.runtime.atn.PredictionMode;\n\npublic class Parser {\n\n  private final ParsingContext pc = new ParsingContext();\n\n  private FormulogParser getParser(Reader r, boolean isTsv) throws ParseException {\n    try {\n      CharStream chars = CharStreams.fromReader(r);\n      FormulogLexer lexer = new FormulogLexer(chars);\n      TokenStream tokens = isTsv ? new BufferedTokenStream(lexer) : new CommonTokenStream(lexer);\n      return new FormulogParser(tokens);\n    } catch (IOException e) {\n      throw new ParseException(0, e.getMessage());\n    }\n  }\n\n  public BasicProgram parse(Reader r) throws ParseException {\n    return parse(r, Collections.emptyList());\n  }\n\n  public BasicProgram parse(Reader r, List<Path> inputDirs) throws ParseException {\n    try {\n      FormulogParser parser = getParser(r, false);\n      ProgContext progCtx = parser.prog();\n      Pair<BasicProgram, Set<RelationSymbol>> p = new TopLevelParser(pc).parse(progCtx);\n      BasicProgram prog = p.fst();\n      loadExternalEdbs(prog, p.snd(), inputDirs);\n      return prog;\n    } catch (UncheckedParseException e) {\n      throw new ParseException(e);\n    }\n  }\n\n  public Set<Term[]> parseFacts(RelationSymbol sym, Reader factStream) throws ParseException {\n    Set<Term[]> facts = new HashSet<>();\n    FormulogParser parser = getParser(factStream, true);\n    FactFileParser fpp = new FactFileParser(pc);\n    fpp.loadFacts(parser.tsvFile(), sym.getArity(), facts);\n    return facts;\n  }\n\n  private void loadExternalEdbs(\n      Program<UserPredicate, BasicRule> prog, Set<RelationSymbol> rels, List<Path> inputDirs)\n      throws ParseException {\n    if (rels.isEmpty() || inputDirs.isEmpty()) {\n      return;\n    }\n    ExecutorService exec = Executors.newFixedThreadPool(Main.parallelism);\n    List<Future<?>> tasks = new ArrayList<>();\n    for (Path inputDir : inputDirs) {\n      for (RelationSymbol sym : rels) {\n        tasks.add(\n            exec.submit(\n                new Runnable() {\n\n                  @Override\n                  public void run() {\n                    try {\n                      readEdbFromFile(sym, inputDir, prog.getFacts(sym));\n                    } catch (ParseException e) {\n                      throw new UncheckedParseException(e);\n                    }\n                  }\n                }));\n      }\n    }\n    exec.shutdown();\n    try {\n      for (Future<?> task : tasks) {\n        task.get();\n      }\n    } catch (InterruptedException e) {\n      throw new ParseException(0, e.getMessage());\n    } catch (ExecutionException e) {\n      Throwable cause = e.getCause();\n      if (cause instanceof UncheckedParseException) {\n        throw new ParseException((UncheckedParseException) cause);\n      }\n      throw new ParseException(0, e.getMessage());\n    }\n  }\n\n  private void readEdbFromFile(RelationSymbol sym, Path inputDir, Set<Term[]> acc)\n      throws ParseException {\n    Path path = inputDir.resolve(sym.toString() + \".tsv\");\n    try (FileReader fr = new FileReader(path.toFile())) {\n      FormulogParser parser = getParser(fr, true);\n      parser.getInterpreter().setPredictionMode(PredictionMode.SLL);\n      FactFileParser fpp = new FactFileParser(pc);\n      fpp.loadFacts(parser.tsvFile(), sym.getArity(), acc);\n    } catch (FileNotFoundException e) {\n      throw new ParseException(0, \"Could not find external fact file: \" + path);\n    } catch (IOException e) {\n      throw new ParseException(path.toString(), 0, e.getMessage());\n    } catch (UncheckedParseException e) {\n      throw new ParseException(path.toString(), e.getLineNo(), e.getMessage());\n    } catch (ParseException e) {\n      throw new ParseException(path.toString(), e.getLineNo(), e.getMessage());\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/parsing/ParsingContext.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.parsing;\n\nimport edu.harvard.seas.pl.formulog.ast.FunctionCallFactory;\nimport edu.harvard.seas.pl.formulog.functions.FunctionDefManager;\nimport edu.harvard.seas.pl.formulog.symbols.ConstructorSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.FunctionSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.SymbolManager;\nimport edu.harvard.seas.pl.formulog.types.TypeManager;\nimport edu.harvard.seas.pl.formulog.types.Types.AlgebraicDataType;\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nclass ParsingContext {\n\n  private final SymbolManager sm = new SymbolManager();\n  private final FunctionDefManager fdm = new FunctionDefManager();\n  private final FunctionCallFactory fcf = new FunctionCallFactory(fdm);\n  private final Map<FunctionSymbol, Pair<AlgebraicDataType, Integer>> rl = new HashMap<>();\n  private final Map<ConstructorSymbol, FunctionSymbol[]> cl = new HashMap<>();\n  private final TypeManager tm = new TypeManager();\n  private final Map<String, AtomicInteger> nfc = new HashMap<>();\n\n  public SymbolManager symbolManager() {\n    return sm;\n  }\n\n  public FunctionCallFactory functionCallFactory() {\n    return fcf;\n  }\n\n  public FunctionDefManager functionDefManager() {\n    return fdm;\n  }\n\n  public Map<FunctionSymbol, Pair<AlgebraicDataType, Integer>> recordLabels() {\n    return rl;\n  }\n\n  public Map<ConstructorSymbol, FunctionSymbol[]> constructorLabels() {\n    return cl;\n  }\n\n  public TypeManager typeManager() {\n    return tm;\n  }\n\n  public Map<String, AtomicInteger> nestedFunctionCounters() {\n    return nfc;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/parsing/ParsingUtil.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.parsing;\n\nimport static edu.harvard.seas.pl.formulog.util.Util.map;\n\nimport edu.harvard.seas.pl.formulog.ast.Var;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogBaseVisitor;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.FunDefLHSContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.IntParamContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.ParameterContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.ParameterListContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.TypeParamContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.WildCardParamContext;\nimport edu.harvard.seas.pl.formulog.symbols.FunctionSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.parameterized.Param;\nimport edu.harvard.seas.pl.formulog.types.FunctorType;\nimport edu.harvard.seas.pl.formulog.types.Types.Type;\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nfinal class ParsingUtil {\n\n  private ParsingUtil() {\n    throw new AssertionError();\n  }\n\n  public static List<Param> extractParams(ParsingContext pc, ParameterListContext ctx) {\n    List<Param> l = new ArrayList<>();\n    for (ParameterContext param : ctx.parameter()) {\n      l.add(extractParam(pc, param));\n    }\n    return l;\n  }\n\n  public static Param extractParam(ParsingContext pc, ParameterContext ctx) {\n    return ctx.accept(\n        new FormulogBaseVisitor<Param>() {\n\n          @Override\n          public Param visitWildCardParam(WildCardParamContext ctx) {\n            return Param.wildCard();\n          }\n\n          @Override\n          public Param visitTypeParam(TypeParamContext ctx) {\n            TypeExtractor typeExtractor = new TypeExtractor(pc);\n            return Param.wildCard(typeExtractor.extract(ctx.type()));\n          }\n\n          @Override\n          public Param visitIntParam(IntParamContext ctx) {\n            return Param.nat(Integer.parseInt(ctx.INT().getText()));\n          }\n        });\n  }\n\n  public static Pair<FunctionSymbol, List<Var>> extractFunDeclaration(\n      ParsingContext pc, FunDefLHSContext ctx, boolean isNested) {\n    String name = ctx.ID().getText();\n    if (pc.symbolManager().hasConstructorSymbolWithName(name)) {\n      throw new RuntimeException(\"Cannot create a function with name of constructor: \" + name);\n    }\n    if (isNested) {\n      AtomicInteger cnt =\n          Util.lookupOrCreate(pc.nestedFunctionCounters(), name, AtomicInteger::new);\n      name += \"$\" + cnt.getAndIncrement();\n    }\n    TypeExtractor typeExtractor = new TypeExtractor(pc);\n    List<Type> argTypes = typeExtractor.extract(ctx.args.type());\n    Type retType = typeExtractor.extract(ctx.retType);\n    FunctionSymbol sym =\n        pc.symbolManager()\n            .createFunctionSymbol(name, argTypes.size(), new FunctorType(argTypes, retType));\n    List<Var> args = map(ctx.args.var(), x -> Var.fresh(x.getText()));\n    if (args.size() != new HashSet<>(args).size()) {\n      throw new RuntimeException(\n          \"Cannot use the same variable multiple times in a function declaration: \" + name);\n    }\n    return new Pair<>(sym, args);\n  }\n\n  public static List<Pair<FunctionSymbol, List<Var>>> extractFunDeclarations(\n      ParsingContext pc, List<FunDefLHSContext> ctxs, boolean isNested) {\n    return map(ctxs, ctx -> extractFunDeclaration(pc, ctx, isNested));\n  }\n\n  public static Map<String, Identifier> varsToIds(Iterable<Var> vars) {\n    Map<String, Identifier> m = new HashMap<>();\n    for (Var x : vars) {\n      m.put(x.getName(), Identifier.make(x));\n    }\n    return m;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/parsing/TermExtractor.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2024 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.parsing;\n\nimport edu.harvard.seas.pl.formulog.ast.BoolTerm;\nimport edu.harvard.seas.pl.formulog.ast.Constructors;\nimport edu.harvard.seas.pl.formulog.ast.FP32;\nimport edu.harvard.seas.pl.formulog.ast.FP64;\nimport edu.harvard.seas.pl.formulog.ast.Fold;\nimport edu.harvard.seas.pl.formulog.ast.I32;\nimport edu.harvard.seas.pl.formulog.ast.I64;\nimport edu.harvard.seas.pl.formulog.ast.LetFunExpr;\nimport edu.harvard.seas.pl.formulog.ast.MatchClause;\nimport edu.harvard.seas.pl.formulog.ast.MatchExpr;\nimport edu.harvard.seas.pl.formulog.ast.NestedFunctionDef;\nimport edu.harvard.seas.pl.formulog.ast.StringTerm;\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.ast.Terms;\nimport edu.harvard.seas.pl.formulog.ast.Var;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogBaseVisitor;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.BinopFormulaContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.BinopTermContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.ConsTermContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.DoubleTermContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.FloatTermContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.FoldTermContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.FormulaTermContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.FunDefLHSContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.HoleTermContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.I32TermContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.I64TermContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.IfExprContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.IndexedFunctorContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.IteTermContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.LetExprContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.LetFormulaContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.LetFunExprContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.ListTermContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.MatchClauseContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.MatchExprContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.NonEmptyTermListContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.NotFormulaContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.OutermostCtorContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.ParensTermContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.QuantifiedFormulaContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.RecordEntryContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.RecordTermContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.RecordUpdateTermContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.SpecialFPTermContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.StringTermContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.TermContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.TermSymFormulaContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.TupleTermContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.UnopTermContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.VarTermContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogVisitor;\nimport edu.harvard.seas.pl.formulog.symbols.BuiltInConstructorSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.BuiltInFunctionSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.ConstructorSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.FunctionSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.GlobalSymbolManager;\nimport edu.harvard.seas.pl.formulog.symbols.RelationSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.Symbol;\nimport edu.harvard.seas.pl.formulog.symbols.parameterized.BuiltInConstructorSymbolBase;\nimport edu.harvard.seas.pl.formulog.symbols.parameterized.Param;\nimport edu.harvard.seas.pl.formulog.symbols.parameterized.ParamKind;\nimport edu.harvard.seas.pl.formulog.symbols.parameterized.ParameterizedConstructorSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.parameterized.ParameterizedSymbol;\nimport edu.harvard.seas.pl.formulog.types.Types.AlgebraicDataType;\nimport edu.harvard.seas.pl.formulog.types.Types.Type;\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport edu.harvard.seas.pl.formulog.util.StackMap;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Function;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nclass TermExtractor {\n\n  private final ParsingContext pc;\n  private final StackMap<String, Identifier> env = new StackMap<>();\n\n  public TermExtractor(ParsingContext parsingContext) {\n    pc = parsingContext;\n    env.push(new HashMap<>());\n  }\n\n  public synchronized Term extract(TermContext ctx) {\n    try {\n      Term t = visitor.visit(ctx);\n      return t;\n    } catch (UncheckedParseException e) {\n      throw e;\n    } catch (Exception e) {\n      throw new UncheckedParseException(ctx.start.getLine(), e.getMessage());\n    }\n  }\n\n  public synchronized List<Term> extractList(List<TermContext> ctxs) {\n    List<Term> terms = new ArrayList<>();\n    for (TermContext ctx : ctxs) {\n      terms.add(extract(ctx));\n    }\n    return terms;\n  }\n\n  public synchronized Term[] extractArray(List<TermContext> ctxs) {\n    Term[] terms = new Term[ctxs.size()];\n    int i = 0;\n    for (TermContext ctx : ctxs) {\n      terms[i] = visitor.visit(ctx);\n      i++;\n    }\n    return terms;\n  }\n\n  public synchronized void pushIds(Map<String, Identifier> ids) {\n    env.push(ids);\n  }\n\n  public synchronized Map<String, Identifier> popIds() {\n    return env.pop();\n  }\n\n  private final FormulogVisitor<Term> visitor =\n      new FormulogBaseVisitor<Term>() {\n\n        private boolean inFormula;\n        private boolean inPattern;\n\n        private void assertNotInFormula(int lineNo, String msg) {\n          if (inFormula) {\n            throw new UncheckedParseException(lineNo, msg);\n          }\n        }\n\n        private void toggleInFormula() {\n          inFormula = !inFormula;\n        }\n\n        @Override\n        public Term visitHoleTerm(HoleTermContext ctx) {\n          return Var.makeHole();\n        }\n\n        @Override\n        public Term visitVarTerm(VarTermContext ctx) {\n          String name = ctx.VAR().getText();\n          Identifier id = env.get(name);\n          if (id == null) {\n            id = Identifier.make(Var.fresh(name));\n            env.put(name, id);\n          }\n          assert id.isVar();\n          return id.asVar();\n        }\n\n        @Override\n        public Term visitStringTerm(StringTermContext ctx) {\n          String s = ctx.QSTRING().getText();\n          return StringTerm.make(s.substring(1, s.length() - 1));\n        }\n\n        @Override\n        public Term visitConsTerm(ConsTermContext ctx) {\n          Term[] args = extractArray(ctx.term());\n          return Constructors.make(BuiltInConstructorSymbol.CONS, args);\n        }\n\n        private boolean isSpecialFormulaCtor(String name) {\n          if (pc.symbolManager().hasName(name)) {\n            Symbol sym = pc.symbolManager().lookupSymbol(name);\n            return sym instanceof ConstructorSymbol\n                && isSpecialFormulaCtor((ConstructorSymbol) sym);\n          }\n          return false;\n        }\n\n        // For a couple constructors, we want to make sure that their arguments are\n        // forced to be non-formula types. For example, the constructor bv_const needs\n        // to take something of type i32, not i32 smt.\n        private boolean isSpecialFormulaCtor(ConstructorSymbol sym) {\n          if (sym instanceof ParameterizedConstructorSymbol) {\n            switch (((ParameterizedConstructorSymbol) sym).getBase()) {\n              case BV_BIG_CONST:\n              case BV_CONST:\n              case BV_EXTRACT:\n              case FP_BIG_CONST:\n              case FP_CONST:\n                return true;\n              default:\n                break;\n            }\n          } else if (sym instanceof BuiltInConstructorSymbol) {\n            switch ((BuiltInConstructorSymbol) sym) {\n              case INT_BIG_CONST:\n              case INT_CONST:\n                return true;\n              default:\n                break;\n            }\n          }\n          return false;\n        }\n\n        @Override\n        public Term visitIndexedFunctor(IndexedFunctorContext ctx) {\n          String name = ctx.id.getText();\n          if (name.equals(\"true\") || name.equals(\"false\")) {\n            return parseBool(ctx);\n          }\n          boolean wasInFormula = inFormula;\n          boolean isSpecial = isSpecialFormulaCtor(name);\n          if (isSpecial) {\n            inFormula = false;\n          }\n          List<Param> params = ParsingUtil.extractParams(pc, ctx.parameterList());\n          Term[] args = extractArray(ctx.termArgs().term());\n          if (inPattern\n              && args.length == 0\n              && !pc.symbolManager().hasConstructorSymbolWithName(name)) {\n            Var x = Var.fresh(name);\n            env.put(name, Identifier.make(x));\n          }\n          Identifier id = env.get(name);\n          if (id != null && id.isVar()) {\n            if (args.length > 0) {\n              throw new UncheckedParseException(\n                  ctx.start.getLine(), \"Cannot apply a variable \" + name + \" to arguments.\");\n            }\n            return id.asVar();\n          }\n          if (name.charAt(0) == '#' && args.length == 0) {\n            if (params.size() != 1) {\n              throw new IllegalArgumentException(\n                  \"Expected a single parameter to solver variable: \" + name);\n            }\n            Type ty = params.get(0).getType();\n            return extractSolverSymbol(StringTerm.make(name.substring(1)), ty);\n          }\n          Symbol sym = id != null ? id.asFunctionSymbol() : pc.symbolManager().lookupSymbol(name);\n          if (sym instanceof ParameterizedSymbol) {\n            sym = ((ParameterizedSymbol) sym).copyWithNewArgs(params);\n          } else if (!params.isEmpty()) {\n            throw new UncheckedParseException(\n                ctx.start.getLine(), \"Symbol \" + sym + \" is not parametric.\");\n          }\n          adjustFunctorArgs(sym, args);\n          Term t = makeFunctor(ctx.start.getLine(), sym, args);\n          if (isSpecial) {\n            t = makeExitFormula(t);\n          }\n          inFormula = wasInFormula;\n          return t;\n        }\n\n        private void adjustFunctorArgs(Symbol sym, Term[] args) {\n          if (sym instanceof ParameterizedConstructorSymbol) {\n            switch (((ParameterizedConstructorSymbol) sym).getBase()) {\n              case BV_EXTRACT:\n                args[0] = makeEnterFormula(args[0]);\n                break;\n              default:\n                break;\n            }\n          }\n        }\n\n        private Term parseBool(IndexedFunctorContext ctx) {\n          String name = ctx.id.getText();\n          assert name.equals(\"true\") || name.equals(\"false\");\n          boolean val = name.equals(\"true\");\n          if (!ctx.parameterList().parameter().isEmpty()) {\n            throw new UncheckedParseException(\n                ctx.start.getLine(), \"Boolean value \" + val + \" cannot be parameterized\");\n          }\n          if (!ctx.termArgs().term().isEmpty()) {\n            throw new UncheckedParseException(\n                ctx.start.getLine(), \"Boolean value \" + val + \" cannot be applied to arguments\");\n          }\n          return BoolTerm.mk(val);\n        }\n\n        private Term makeFunctor(int lineNo, Symbol sym, Term[] args) {\n          if (sym.getArity() != args.length) {\n            throw new UncheckedParseException(\n                lineNo,\n                \"Symbol \"\n                    + sym\n                    + \" has arity \"\n                    + sym.getArity()\n                    + \", but applied to \"\n                    + args.length\n                    + \" arg(s): \"\n                    + Arrays.toString(args));\n          }\n          if (sym instanceof RelationSymbol) {\n            FunctionSymbol newSym =\n                pc.symbolManager().createPredicateFunctionSymbolPlaceholder((RelationSymbol) sym);\n            return pc.functionCallFactory().make(newSym, args);\n          } else if (sym instanceof FunctionSymbol) {\n            Term t = pc.functionCallFactory().make((FunctionSymbol) sym, args);\n            if (sym.getArity() > 0) {\n              assertNotInFormula(\n                  lineNo, \"Cannot invoke a non-nullary function from within a formula: \" + t);\n            }\n            return t;\n          } else if (sym instanceof ConstructorSymbol) {\n            ConstructorSymbol csym = (ConstructorSymbol) sym;\n            Term t = Constructors.make(csym, args);\n            return t;\n          } else {\n            throw new UncheckedParseException(\n                lineNo,\n                \"Cannot create a term with non-constructor, non-function symbol \" + sym + \".\");\n          }\n        }\n\n        @Override\n        public Term visitFoldTerm(FoldTermContext ctx) {\n          assertNotInFormula(\n              ctx.start.getLine(), \"Cannot invoke a fold from within a formula: \" + ctx.getText());\n          String name = ctx.ID().getText();\n          Identifier id = env.get(name);\n          if (id != null && id.isVar()) {\n            throw new UncheckedParseException(\n                ctx.start.getLine(), \"Cannot use a variable as the function to fold: \" + name);\n          }\n          Symbol sym = id != null ? id.asFunctionSymbol() : pc.symbolManager().lookupSymbol(name);\n          if (!(sym instanceof FunctionSymbol)) {\n            throw new UncheckedParseException(\n                ctx.start.getLine(), \"Cannot fold over non-function: \" + sym);\n          }\n          if (sym.getArity() != 2) {\n            throw new UncheckedParseException(\n                ctx.start.getLine(),\n                \"Can only fold over a binary function, but \"\n                    + sym\n                    + \" has arity \"\n                    + sym.getArity());\n          }\n          Term[] args = extractArray(ctx.termArgs().term());\n          return Fold.mk((FunctionSymbol) sym, args, pc.functionCallFactory());\n        }\n\n        @Override\n        public Term visitTupleTerm(TupleTermContext ctx) {\n          Term[] args = extractArray(ctx.tuple().term());\n          return Constructors.make(GlobalSymbolManager.lookupTupleSymbol(args.length), args);\n        }\n\n        private final Pattern hex = Pattern.compile(\"0x([0-9a-fA-F]+)[lL]?\");\n\n        @Override\n        public Term visitI32Term(I32TermContext ctx) {\n          Matcher m = hex.matcher(ctx.val.getText());\n          int n;\n          if (m.matches()) {\n            n = Integer.parseUnsignedInt(m.group(1).toUpperCase(), 16);\n          } else {\n            n = Integer.parseInt(ctx.val.getText());\n          }\n          return I32.make(n);\n        }\n\n        @Override\n        public Term visitI64Term(I64TermContext ctx) {\n          Matcher m = hex.matcher(ctx.val.getText());\n          long n;\n          if (m.matches()) {\n            n = Long.parseUnsignedLong(m.group(1).toUpperCase(), 16);\n          } else {\n            // Long.parseLong does not allow trailing l or L.\n            String text = ctx.val.getText();\n            String sub = text.substring(0, text.length() - 1);\n            n = Long.parseLong(sub);\n          }\n          return I64.make(n);\n        }\n\n        @Override\n        public Term visitFloatTerm(FloatTermContext ctx) {\n          return FP32.make(Float.parseFloat(ctx.val.getText()));\n        }\n\n        @Override\n        public Term visitDoubleTerm(DoubleTermContext ctx) {\n          return FP64.make(Double.parseDouble(ctx.getText()));\n        }\n\n        @Override\n        public Term visitSpecialFPTerm(SpecialFPTermContext ctx) {\n          switch (ctx.val.getType()) {\n            case FormulogParser.FP32_NAN:\n              return FP32.make(Float.NaN);\n            case FormulogParser.FP32_NEG_INFINITY:\n              return FP32.make(Float.NEGATIVE_INFINITY);\n            case FormulogParser.FP32_POS_INFINITY:\n              return FP32.make(Float.POSITIVE_INFINITY);\n            case FormulogParser.FP64_NAN:\n              return FP64.make(Double.NaN);\n            case FormulogParser.FP64_NEG_INFINITY:\n              return FP64.make(Double.NEGATIVE_INFINITY);\n            case FormulogParser.FP64_POS_INFINITY:\n              return FP64.make(Double.POSITIVE_INFINITY);\n          }\n          throw new AssertionError();\n        }\n\n        @Override\n        public Term visitRecordTerm(RecordTermContext ctx) {\n          Pair<ConstructorSymbol, Map<Integer, Term>> p =\n              handleRecordEntries(ctx.recordEntries().recordEntry());\n          ConstructorSymbol csym = p.fst();\n          Map<Integer, Term> argMap = p.snd();\n          Term[] args = new Term[csym.getArity()];\n          if (args.length != argMap.keySet().size()) {\n            throw new UncheckedParseException(\n                ctx.start.getLine(), \"Missing label(s) when creating record of type \" + csym);\n          }\n          for (int i = 0; i < args.length; i++) {\n            args[i] = argMap.get(i);\n          }\n          return Constructors.make(csym, args);\n        }\n\n        @Override\n        public Term visitRecordUpdateTerm(RecordUpdateTermContext ctx) {\n          Pair<ConstructorSymbol, Map<Integer, Term>> p =\n              handleRecordEntries(ctx.recordEntries().recordEntry());\n          ConstructorSymbol csym = p.fst();\n          Map<Integer, Term> argMap = p.snd();\n          Term[] args = new Term[csym.getArity()];\n          FunctionSymbol[] labels = pc.constructorLabels().get(csym);\n          Term orig = extract(ctx.term());\n          for (int i = 0; i < args.length; ++i) {\n            Term t = argMap.get(i);\n            if (t == null) {\n              FunctionSymbol label = labels[i];\n              t = pc.functionCallFactory().make(label, Terms.singletonArray(orig));\n            }\n            args[i] = t;\n          }\n          return Constructors.make(csym, args);\n        }\n\n        private Pair<ConstructorSymbol, Map<Integer, Term>> handleRecordEntries(\n            List<RecordEntryContext> entries) {\n          AlgebraicDataType type = null;\n          Map<Integer, Term> argMap = new HashMap<>();\n          for (RecordEntryContext entry : entries) {\n            Symbol label = pc.symbolManager().lookupSymbol(entry.ID().getText());\n            Pair<AlgebraicDataType, Integer> p = pc.recordLabels().get(label);\n            if (p == null) {\n              throw new UncheckedParseException(\n                  entry.start.getLine(), label + \" is not a record label\");\n            }\n            AlgebraicDataType type2 = p.fst();\n            if (type == null) {\n              type = type2;\n            } else if (!type.equals(type2)) {\n              throw new UncheckedParseException(\n                  entry.start.getLine(),\n                  \"Cannot use label \" + label + \" in a record of type \" + type);\n            }\n            if (argMap.putIfAbsent(p.snd(), extract(entry.term())) != null) {\n              throw new UncheckedParseException(\n                  entry.start.getLine(),\n                  \"Cannot use the same label \" + label + \" multiple times when creating a record\");\n            }\n          }\n          ConstructorSymbol csym = type.getConstructors().iterator().next().getSymbol();\n          return new Pair<>(csym, argMap);\n        }\n\n        @Override\n        public Term visitUnopTerm(UnopTermContext ctx) {\n          Term t = ctx.term().accept(this);\n          Term r = null;\n          var start = ctx.term().getStart().getType();\n          switch (ctx.op.getType()) {\n            case FormulogParser.BANG:\n              r = pc.functionCallFactory().make(BuiltInFunctionSymbol.BNOT, new Term[] {t});\n              break;\n            case FormulogParser.PLUS:\n              if ((t instanceof I32 && start != FormulogParser.HEX)\n                  || (t instanceof I64 && start != FormulogParser.HEXL)\n                  || (t instanceof FP32)\n                  || (t instanceof FP64)) {\n                return t;\n              }\n              break;\n            case FormulogParser.MINUS:\n              if (t instanceof I32 && start != FormulogParser.HEX) {\n                return I32.make(-((I32) t).getVal());\n              } else if (t instanceof I64 && start != FormulogParser.HEXL) {\n                return I64.make(-((I64) t).getVal());\n              } else if (t instanceof FP32) {\n                return FP32.make(-((FP32) t).getVal());\n              } else if (t instanceof FP64) {\n                return FP64.make(-((FP64) t).getVal());\n              }\n              r = pc.functionCallFactory().make(BuiltInFunctionSymbol.I32_NEG, new Term[] {t});\n              break;\n          }\n          if (r == null) {\n            throw new UncheckedParseException(\n                ctx.start.getLine(), \"Unrecognized unop: \" + ctx.getText());\n          }\n          assertNotInFormula(\n              ctx.start.getLine(), \"Cannot invoke a unop from within a formula: \" + ctx.getText());\n          return r;\n        }\n\n        private Term makeBoolMatch(Term matchee, Term ifTrue, Term ifFalse) {\n          MatchClause matchTrue = MatchClause.make(BoolTerm.mkTrue(), ifTrue);\n          MatchClause matchFalse = MatchClause.make(BoolTerm.mkFalse(), ifFalse);\n          return MatchExpr.make(matchee, Arrays.asList(matchTrue, matchFalse));\n        }\n\n        private FunctionSymbol tokenToBinopSym(int tokenType) {\n          switch (tokenType) {\n            case FormulogParser.MUL:\n              return BuiltInFunctionSymbol.I32_MUL;\n            case FormulogParser.DIV:\n              return BuiltInFunctionSymbol.I32_SDIV;\n            case FormulogParser.REM:\n              return BuiltInFunctionSymbol.I32_SREM;\n            case FormulogParser.PLUS:\n              return BuiltInFunctionSymbol.I32_ADD;\n            case FormulogParser.MINUS:\n              return BuiltInFunctionSymbol.I32_SUB;\n            case FormulogParser.AMP:\n              return BuiltInFunctionSymbol.I32_AND;\n            case FormulogParser.CARET:\n              return BuiltInFunctionSymbol.I32_XOR;\n            case FormulogParser.EQ:\n              return BuiltInFunctionSymbol.BEQ;\n            case FormulogParser.NEQ:\n              return BuiltInFunctionSymbol.BNEQ;\n            case FormulogParser.LT:\n              return BuiltInFunctionSymbol.I32_LT;\n            case FormulogParser.LTE:\n              return BuiltInFunctionSymbol.I32_LE;\n            case FormulogParser.GT:\n              return BuiltInFunctionSymbol.I32_GT;\n            case FormulogParser.GTE:\n              return BuiltInFunctionSymbol.I32_GE;\n            default:\n              return null;\n          }\n        }\n\n        private Term makeNonFunctionBinop(int tokenType, Term lhs, Term rhs) {\n          switch (tokenType) {\n            case FormulogParser.AMPAMP:\n              return makeBoolMatch(lhs, rhs, BoolTerm.mkFalse());\n            case FormulogParser.BARBAR:\n              return makeBoolMatch(lhs, BoolTerm.mkTrue(), rhs);\n            default:\n              return null;\n          }\n        }\n\n        @Override\n        public Term visitBinopTerm(BinopTermContext ctx) {\n          Term[] args = {extract(ctx.term(0)), extract(ctx.term(1))};\n          FunctionSymbol sym = tokenToBinopSym(ctx.op.getType());\n          Term t;\n          if (sym == null) {\n            t = makeNonFunctionBinop(ctx.op.getType(), args[0], args[1]);\n          } else {\n            t = pc.functionCallFactory().make(sym, args);\n          }\n          if (t == null) {\n            throw new AssertionError(\"Unrecognized binop: \" + ctx.getText());\n          }\n          assertNotInFormula(\n              ctx.start.getLine(), \"Cannot invoke a binop from within a formula: \" + ctx.getText());\n          return t;\n        }\n\n        @Override\n        public Term visitListTerm(ListTermContext ctx) {\n          Term t = Constructors.makeZeroAry(BuiltInConstructorSymbol.NIL);\n          List<TermContext> ctxs = new ArrayList<>(ctx.list().term());\n          Collections.reverse(ctxs);\n          for (TermContext tc : ctxs) {\n            t = Constructors.make(BuiltInConstructorSymbol.CONS, new Term[] {extract(tc), t});\n          }\n          return t;\n        }\n\n        @Override\n        public Term visitParensTerm(ParensTermContext ctx) {\n          return extract(ctx.term());\n        }\n\n        private Term makeExitFormula(Term t) {\n          return Constructors.make(BuiltInConstructorSymbol.EXIT_FORMULA, Terms.singletonArray(t));\n        }\n\n        private Term makeEnterFormula(Term t) {\n          return Constructors.make(BuiltInConstructorSymbol.ENTER_FORMULA, Terms.singletonArray(t));\n        }\n\n        @Override\n        public Term visitFormulaTerm(FormulaTermContext ctx) {\n          assertNotInFormula(\n              ctx.start.getLine(), \"Cannot nest a formula within a formula: \" + ctx.getText());\n          toggleInFormula();\n          Term t = extract(ctx.term());\n          t = makeEnterFormula(t);\n          toggleInFormula();\n          return t;\n        }\n\n        @Override\n        public Term visitNotFormula(NotFormulaContext ctx) {\n          Term t = extract(ctx.term());\n          return Constructors.make(BuiltInConstructorSymbol.SMT_NOT, Terms.singletonArray(t));\n        }\n\n        @Override\n        public Term visitBinopFormula(BinopFormulaContext ctx) {\n          Term[] args = extractArray(ctx.term());\n          ConstructorSymbol sym;\n          switch (ctx.op.getType()) {\n            case FormulogParser.FORMULA_EQ:\n            case FormulogParser.IFF:\n              sym =\n                  (ConstructorSymbol)\n                      pc.symbolManager()\n                          .getParameterizedSymbol(BuiltInConstructorSymbolBase.SMT_EQ);\n              break;\n            case FormulogParser.IMP:\n              sym = BuiltInConstructorSymbol.SMT_IMP;\n              break;\n            case FormulogParser.AND:\n              sym = BuiltInConstructorSymbol.SMT_AND;\n              break;\n            case FormulogParser.OR:\n              sym = BuiltInConstructorSymbol.SMT_OR;\n              break;\n            default:\n              throw new AssertionError();\n          }\n          return Constructors.make(sym, args);\n        }\n\n        @Override\n        public Term visitLetFormula(LetFormulaContext ctx) {\n          ConstructorSymbol sym =\n              (ConstructorSymbol)\n                  pc.symbolManager().getParameterizedSymbol(BuiltInConstructorSymbolBase.SMT_LET);\n          boolean wasInFormula = inFormula;\n          inFormula = false;\n          Term[] args = extractArray(ctx.term());\n          inFormula = wasInFormula;\n          args[1] = makeEnterFormula(args[1]);\n          args[2] = makeEnterFormula(args[2]);\n          return makeExitFormula(Constructors.make(sym, args));\n        }\n\n        @Override\n        public Term visitQuantifiedFormula(QuantifiedFormulaContext ctx) {\n          Term[] args = new Term[3];\n          args[0] = parseFormulaVarList(ctx.variables);\n          args[1] = makeEnterFormula(extract(ctx.boundTerm));\n          args[2] = Constructors.nil();\n          if (ctx.pattern != null) {\n            args[2] =\n                Constructors.make(\n                    BuiltInConstructorSymbol.CONS,\n                    new Term[] {(parsePatternList(ctx.pattern)), args[2]});\n          }\n          ConstructorSymbol sym;\n          switch (ctx.quantifier.getType()) {\n            case FormulogParser.FORALL:\n              sym = BuiltInConstructorSymbol.SMT_FORALL;\n              break;\n            case FormulogParser.EXISTS:\n              sym = BuiltInConstructorSymbol.SMT_EXISTS;\n              break;\n            default:\n              throw new AssertionError(\"impossible\");\n          }\n          return makeExitFormula(Constructors.make(sym, args));\n        }\n\n        private Term parsePatternList(NonEmptyTermListContext ctx) {\n          return parseNonEmptyTermList(\n              ctx,\n              pat -> {\n                ConstructorSymbol sym =\n                    (ConstructorSymbol)\n                        pc.symbolManager()\n                            .getParameterizedSymbol(BuiltInConstructorSymbolBase.SMT_PAT);\n                return Constructors.make(sym, Terms.singletonArray(makeEnterFormula(pat)));\n              });\n        }\n\n        private Term parseFormulaVarList(NonEmptyTermListContext ctx) {\n          return parseNonEmptyTermList(\n              ctx,\n              var -> {\n                ConstructorSymbol sym =\n                    (ConstructorSymbol)\n                        pc.symbolManager()\n                            .getParameterizedSymbol(BuiltInConstructorSymbolBase.SMT_WRAP_VAR);\n                return Constructors.make(sym, Terms.singletonArray(var));\n              });\n        }\n\n        private Term parseNonEmptyTermList(\n            NonEmptyTermListContext ctx, Function<Term, Term> transformer) {\n          Term acc = Constructors.nil();\n          List<TermContext> ctxs = new ArrayList<>(ctx.term());\n          Collections.reverse(ctxs);\n          for (TermContext tc : ctxs) {\n            Term t = transformer.apply(extract(tc));\n            acc = Constructors.make(BuiltInConstructorSymbol.CONS, new Term[] {t, acc});\n          }\n          return acc;\n        }\n\n        @Override\n        public Term visitIteTerm(IteTermContext ctx) {\n          Term[] args = extractArray(ctx.term());\n          if (inFormula) {\n            return Constructors.make(BuiltInConstructorSymbol.SMT_ITE, args);\n          } else {\n            return makeBoolMatch(args[0], args[1], args[2]);\n          }\n        }\n\n        @Override\n        public Term visitTermSymFormula(TermSymFormulaContext ctx) {\n          Type type = ParsingUtil.extractParam(pc, ctx.parameter()).getType();\n          Term id = extract(ctx.term());\n          return extractSolverSymbol(id, type);\n        }\n\n        private Term extractSolverSymbol(Term id, Type type) {\n          ParameterizedConstructorSymbol sym =\n              GlobalSymbolManager.getParameterizedSymbol(BuiltInConstructorSymbolBase.SMT_VAR);\n          sym = sym.copyWithNewArgs(Param.wildCard(), new Param(type, ParamKind.PRE_SMT_TYPE));\n          return makeExitFormula(Constructors.make(sym, Terms.singletonArray(id)));\n        }\n\n        public Term visitOutermostCtor(OutermostCtorContext ctx) {\n          Symbol sym = pc.symbolManager().lookupSymbol(ctx.ID().getText());\n          if (!(sym instanceof ConstructorSymbol)) {\n            throw new UncheckedParseException(\n                ctx.start.getLine(),\n                \"Cannot use non-constructor symbol \" + sym + \" in a `not` term.\");\n          }\n\n          Term[] vars = new Term[sym.getArity()];\n          for (int i = 0; i < vars.length; ++i) {\n            vars[i] = Var.fresh();\n          }\n          Term pat = Constructors.make((ConstructorSymbol) sym, vars);\n          List<MatchClause> clauses = new ArrayList<>();\n          clauses.add(MatchClause.make(pat, BoolTerm.mkFalse()));\n          clauses.add(MatchClause.make(Var.fresh(), BoolTerm.mkTrue()));\n          Term arg = extract(ctx.term());\n          return MatchExpr.make(arg, clauses);\n        }\n\n        @Override\n        public Term visitMatchExpr(MatchExprContext ctx) {\n          Term guard = ctx.term().accept(this);\n          List<MatchClause> matches = new ArrayList<>();\n          for (MatchClauseContext mcc : ctx.matchClause()) {\n            for (TermContext pc : mcc.patterns().term()) {\n              env.push(new HashMap<>());\n              inPattern = true;\n              Term pattern = extract(pc);\n              inPattern = false;\n              Term rhs = extract(mcc.rhs);\n              env.pop();\n              MatchClause clause = MatchClause.make(pattern, rhs);\n              matches.add(clause);\n            }\n          }\n          return MatchExpr.make(guard, matches);\n        }\n\n        @Override\n        public Term visitLetExpr(LetExprContext ctx) {\n          Term assign = ctx.assign.accept(this);\n          env.push(new HashMap<>());\n          inPattern = true;\n          List<Term> ts = new ArrayList<>();\n          for (TermContext termCtx : ctx.lhs.term()) {\n            ts.add(extract(termCtx));\n          }\n          Term t;\n          if (ts.size() > 1) {\n            t =\n                Constructors.make(\n                    GlobalSymbolManager.lookupTupleSymbol(ts.size()),\n                    ts.toArray(Terms.emptyArray()));\n          } else {\n            t = ts.get(0);\n          }\n          inPattern = false;\n          Term body = ctx.body.accept(this);\n          env.pop();\n\n          MatchClause m = MatchClause.make(t, body);\n          return MatchExpr.make(assign, Collections.singletonList(m));\n        }\n\n        @Override\n        public Term visitLetFunExpr(LetFunExprContext ctx) {\n          if (inFormula) {\n            throw new UncheckedParseException(\n                ctx.start.getLine(),\n                \"Cannot define a function from within a formula:\\n\" + ctx.getText());\n          }\n          List<String> names = new ArrayList<>();\n          for (FunDefLHSContext f : ctx.funDefs().funDefLHS()) {\n            String name = f.ID().getText();\n            if (names.contains(name)) {\n              throw new UncheckedParseException(\n                  ctx.start.getLine(),\n                  \"Cannot use the same name more than once in a mutually-recursive function\"\n                      + \" definition \"\n                      + name);\n            }\n            names.add(name);\n          }\n          List<Pair<FunctionSymbol, List<Var>>> signatures =\n              ParsingUtil.extractFunDeclarations(pc, ctx.funDefs().funDefLHS(), true);\n          Iterator<Pair<FunctionSymbol, List<Var>>> sigIt = signatures.iterator();\n          HashMap<String, Identifier> m = new HashMap<>();\n          for (String name : names) {\n            m.put(name, Identifier.make(sigIt.next().fst()));\n          }\n          env.push(m);\n          sigIt = signatures.iterator();\n          Set<NestedFunctionDef> defs = new HashSet<>();\n          for (TermContext bodyCtx : ctx.funDefs().term()) {\n            Pair<FunctionSymbol, List<Var>> p = sigIt.next();\n            List<Var> params = p.snd();\n            env.push(ParsingUtil.varsToIds(params));\n            defs.add(NestedFunctionDef.make(p.fst(), params, extract(bodyCtx)));\n            env.pop();\n          }\n          Term letBody = extract(ctx.letFunBody);\n          env.pop();\n          return LetFunExpr.make(defs, letBody);\n        }\n\n        @Override\n        public Term visitIfExpr(IfExprContext ctx) {\n          Term guard = ctx.guard.accept(this);\n          Term thenExpr = ctx.thenExpr.accept(this);\n          Term elseExpr = ctx.elseExpr.accept(this);\n          List<MatchClause> branches = new ArrayList<>();\n          branches.add(MatchClause.make(BoolTerm.mkTrue(), thenExpr));\n          branches.add(MatchClause.make(BoolTerm.mkFalse(), elseExpr));\n          return MatchExpr.make(guard, branches);\n        }\n      };\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/parsing/TopLevelParser.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.parsing;\n\nimport static edu.harvard.seas.pl.formulog.util.Util.map;\n\nimport edu.harvard.seas.pl.formulog.ast.BasicProgram;\nimport edu.harvard.seas.pl.formulog.ast.BasicRule;\nimport edu.harvard.seas.pl.formulog.ast.ComplexLiteral;\nimport edu.harvard.seas.pl.formulog.ast.ComplexLiterals;\nimport edu.harvard.seas.pl.formulog.ast.FunctionCallFactory;\nimport edu.harvard.seas.pl.formulog.ast.FunctionCallFactory.FunctionCall;\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.ast.UnificationPredicate;\nimport edu.harvard.seas.pl.formulog.ast.UserPredicate;\nimport edu.harvard.seas.pl.formulog.ast.Var;\nimport edu.harvard.seas.pl.formulog.functions.FunctionDef;\nimport edu.harvard.seas.pl.formulog.functions.RecordAccessor;\nimport edu.harvard.seas.pl.formulog.functions.UserFunctionDef;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogBaseVisitor;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.AdtDefContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.AnnotationContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.ClauseStmtContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.ConstructorTypeContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.FactStmtContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.FunDeclContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.ProgContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.QueryStmtContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.RecordDefContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.RecordEntryDefContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.RelDeclContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.TermContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.TypeAliasContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.TypeDeclContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.TypeDefLHSContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.TypeDefRHSContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.UninterpFunDeclContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.UninterpSortDeclContext;\nimport edu.harvard.seas.pl.formulog.symbols.BuiltInFunctionSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.BuiltInTypeSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.ConstructorSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.ConstructorSymbolType;\nimport edu.harvard.seas.pl.formulog.symbols.FunctionSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.MutableRelationSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.PredicateFunctionSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.RecordSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.RelationSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.SymbolManager;\nimport edu.harvard.seas.pl.formulog.symbols.TypeSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.TypeSymbolType;\nimport edu.harvard.seas.pl.formulog.types.BuiltInTypes;\nimport edu.harvard.seas.pl.formulog.types.FunctorType;\nimport edu.harvard.seas.pl.formulog.types.TypeAlias;\nimport edu.harvard.seas.pl.formulog.types.Types;\nimport edu.harvard.seas.pl.formulog.types.Types.AlgebraicDataType;\nimport edu.harvard.seas.pl.formulog.types.Types.AlgebraicDataType.ConstructorScheme;\nimport edu.harvard.seas.pl.formulog.types.Types.Type;\nimport edu.harvard.seas.pl.formulog.types.Types.TypeVar;\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nclass TopLevelParser {\n\n  private final ParsingContext pc;\n\n  public TopLevelParser(ParsingContext parsingContext) {\n    pc = parsingContext;\n  }\n\n  public Pair<BasicProgram, Set<RelationSymbol>> parse(ProgContext ctx) throws ParseException {\n    TopLevelVisitor visitor = new TopLevelVisitor();\n    ctx.accept(visitor);\n    BasicProgram prog = visitor.program();\n    return new Pair<>(prog, visitor.externalEdbs);\n  }\n\n  private final class TopLevelVisitor extends FormulogBaseVisitor<Void> {\n\n    private final VariableCheckPass varChecker = new VariableCheckPass(pc.symbolManager());\n    private final TermExtractor termExtractor = new TermExtractor(pc);\n    private final TypeExtractor typeExtractor = new TypeExtractor(pc);\n    private final Map<RelationSymbol, Set<Term[]>> initialFacts = new HashMap<>();\n    private final Map<RelationSymbol, Set<BasicRule>> rules = new HashMap<>();\n    private final Set<RelationSymbol> externalEdbs = new HashSet<>();\n    private final Set<ConstructorSymbol> uninterpFuncSymbols = new HashSet<>();\n    private UserPredicate query;\n\n    @Override\n    public Void visitFunDecl(FunDeclContext ctx) {\n      var ps = ParsingUtil.extractFunDeclarations(pc, ctx.topLevelFunDefs().funDefLHS(), false);\n      if (ctx.topLevelFunDefs().intro.getType() == FormulogParser.CONST\n          && !ps.get(0).snd().isEmpty()) {\n        throw new UncheckedParseException(\n            ctx.start.getLine(),\n            \"Cannot declare a function with 'const' if it takes arguments (use 'fun' instead): \"\n                + ps.get(0).fst());\n      }\n      Iterator<TermContext> bodies = ctx.topLevelFunDefs().term().iterator();\n      for (Pair<FunctionSymbol, List<Var>> p : ps) {\n        FunctionSymbol sym = p.fst();\n        List<Var> args = p.snd();\n        termExtractor.pushIds(ParsingUtil.varsToIds(args));\n        Term body = termExtractor.extract(bodies.next());\n        termExtractor.popIds();\n        try {\n          Term newBody = varChecker.checkFunction(args, body);\n          pc.functionDefManager().register(UserFunctionDef.get(sym, args, newBody));\n        } catch (VariableCheckPassException e) {\n          throw new UncheckedParseException(\n              ctx.start.getLine(),\n              \"Error in definition for function \" + sym + \": \" + e.getMessage() + \"\\n\" + body);\n        }\n      }\n      return null;\n    }\n\n    @Override\n    public Void visitRelDecl(RelDeclContext ctx) {\n      String name = ctx.ID().getText();\n      List<Type> types = typeExtractor.extract(ctx.maybeAnnotatedTypeList().type());\n      if (!Types.getTypeVars(types).isEmpty()) {\n        throw new UncheckedParseException(\n            ctx.start.getLine(),\n            \"Cannot use type variables in the signature of a relation: \" + name);\n      }\n      MutableRelationSymbol sym =\n          pc.symbolManager()\n              .createRelationSymbol(name, types.size(), new FunctorType(types, BuiltInTypes.bool));\n      for (AnnotationContext actx : ctx.annotation()) {\n        switch (actx.getText()) {\n          case \"@bottomup\":\n            sym.setBottomUp();\n            break;\n          case \"@topdown\":\n            sym.setTopDown();\n            break;\n          case \"@disk\":\n            sym.setDisk();\n            break;\n          case \"@edb\":\n            sym.setEdb();\n            break;\n          default:\n            throw new UncheckedParseException(\n                ctx.start.getLine(),\n                \"Unrecognized annotation for predicate \" + sym + \": \" + actx.getText());\n        }\n      }\n      if (sym.isEdbSymbol()) {\n        initialFacts.put(sym, Util.concurrentSet());\n        if (sym.isDisk()) {\n          externalEdbs.add(sym);\n        }\n      } else {\n        rules.put(sym, new HashSet<>());\n      }\n      return null;\n    }\n\n    @Override\n    public Void visitTypeAlias(TypeAliasContext ctx) {\n      Pair<TypeSymbol, List<TypeVar>> p =\n          parseTypeDefLHS(ctx.typeDefLHS(), TypeSymbolType.TYPE_ALIAS);\n      TypeSymbol sym = p.fst();\n      List<TypeVar> typeVars = p.snd();\n      Type type = typeExtractor.extract(ctx.type());\n      if (!typeVars.containsAll(Types.getTypeVars(type))) {\n        throw new UncheckedParseException(\n            ctx.start.getLine(), \"Unbound type variable in definition of \" + sym);\n      }\n      pc.typeManager().registerAlias(new TypeAlias(sym, typeVars, type));\n      return null;\n    }\n\n    @Override\n    public Void visitTypeDecl(TypeDeclContext ctx) {\n      List<Pair<TypeSymbol, List<TypeVar>>> lhss =\n          map(ctx.typeDefLHS(), lhs -> parseTypeDefLHS(lhs, TypeSymbolType.NORMAL_TYPE));\n      Iterator<TypeDefRHSContext> it = ctx.typeDefRHS().iterator();\n      for (Pair<TypeSymbol, List<TypeVar>> p : lhss) {\n        TypeSymbol sym = p.fst();\n        List<TypeVar> typeVars = p.snd();\n        AlgebraicDataType type = AlgebraicDataType.make(sym, new ArrayList<>(typeVars));\n        TypeDefRHSContext rhs = it.next();\n        if (rhs.adtDef() != null) {\n          handleAdtDef(rhs.adtDef(), type, typeVars);\n        } else {\n          handleRecordDef(rhs.recordDef(), type, typeVars);\n        }\n      }\n      return null;\n    }\n\n    private void handleAdtDef(AdtDefContext ctx, AlgebraicDataType type, List<TypeVar> typeVars) {\n      Set<ConstructorScheme> constructors = new HashSet<>();\n      for (ConstructorTypeContext ctc : ctx.constructorType()) {\n        List<Type> typeArgs = typeExtractor.extract(ctc.typeList().type());\n        ConstructorSymbol csym =\n            pc.symbolManager()\n                .createConstructorSymbol(\n                    ctc.ID().getText(),\n                    typeArgs.size(),\n                    ConstructorSymbolType.VANILLA_CONSTRUCTOR,\n                    new FunctorType(typeArgs, type));\n        if (!typeVars.containsAll(Types.getTypeVars(typeArgs))) {\n          throw new UncheckedParseException(\n              ctx.start.getLine(), \"Unbound type variable in definition of \" + csym);\n        }\n        pc.symbolManager()\n            .createConstructorSymbol(\n                \"#is_\" + csym,\n                1,\n                ConstructorSymbolType.SOLVER_CONSTRUCTOR_TESTER,\n                new FunctorType(type, BuiltInTypes.bool));\n        List<ConstructorSymbol> getterSyms = new ArrayList<>();\n        for (int i = 0; i < csym.getArity(); ++i) {\n          FunctorType t = new FunctorType(type, typeArgs.get(i));\n          String name = \"#\" + csym + \"_\" + (i + 1);\n          getterSyms.add(\n              pc.symbolManager()\n                  .createConstructorSymbol(\n                      name, 1, ConstructorSymbolType.SOLVER_CONSTRUCTOR_GETTER, t));\n        }\n        constructors.add(new ConstructorScheme(csym, typeArgs, getterSyms));\n      }\n      AlgebraicDataType.setConstructors(type.getSymbol(), typeVars, constructors);\n    }\n\n    private void handleRecordDef(\n        RecordDefContext ctx, AlgebraicDataType type, List<TypeVar> typeVars) {\n      List<Type> entryTypes = new ArrayList<>();\n      List<ConstructorSymbol> getterSyms = new ArrayList<>();\n      List<FunctionSymbol> labels = new ArrayList<>();\n      int i = 0;\n      for (RecordEntryDefContext entry : ctx.recordEntryDef()) {\n        Type entryType = typeExtractor.extract(entry.type());\n        entryTypes.add(entryType);\n        FunctorType labelType = new FunctorType(type, entryType);\n        FunctionSymbol label =\n            pc.symbolManager().createFunctionSymbol(entry.ID().getText(), 1, labelType);\n        labels.add(label);\n        final int j = i;\n        pc.functionDefManager().register(new RecordAccessor(label, j));\n        ConstructorSymbol getter =\n            pc.symbolManager()\n                .createConstructorSymbol(\n                    \"#\" + label, 1, ConstructorSymbolType.SOLVER_CONSTRUCTOR_GETTER, labelType);\n        getterSyms.add(getter);\n        pc.recordLabels().put(label, new Pair<>(type, i));\n        i++;\n      }\n      TypeSymbol sym = type.getSymbol();\n      if (!typeVars.containsAll(Types.getTypeVars(entryTypes))) {\n        throw new UncheckedParseException(\n            ctx.start.getLine(), \"Unbound type variable in definition of \" + sym);\n      }\n      FunctorType ctype = new FunctorType(entryTypes, type);\n      RecordSymbol csym =\n          pc.symbolManager().createRecordSymbol(\"_rec_\" + sym, entryTypes.size(), ctype, labels);\n      ConstructorScheme ctor = new ConstructorScheme(csym, entryTypes, getterSyms);\n      AlgebraicDataType.setConstructors(sym, typeVars, Collections.singleton(ctor));\n      pc.constructorLabels().put(csym, labels.toArray(new FunctionSymbol[0]));\n    }\n\n    private Pair<TypeSymbol, List<TypeVar>> parseTypeDefLHS(\n        TypeDefLHSContext ctx, TypeSymbolType symType) {\n      List<TypeVar> typeVars = map(ctx.TYPEVAR(), t -> TypeVar.get(t.getText()));\n      TypeSymbol sym =\n          pc.symbolManager().createTypeSymbol(ctx.ID().getText(), typeVars.size(), symType);\n      if (typeVars.size() != (new HashSet<>(typeVars)).size()) {\n        throw new UncheckedParseException(\n            ctx.start.getLine(),\n            \"Cannot use the same type variable multiple times in a type declaration: \" + sym);\n      }\n      return new Pair<>(sym, typeVars);\n    }\n\n    @Override\n    public Void visitUninterpSortDecl(UninterpSortDeclContext ctx) {\n      parseTypeDefLHS(ctx.typeDefLHS(), TypeSymbolType.UNINTERPRETED_SORT);\n      return null;\n    }\n\n    @Override\n    public Void visitUninterpFunDecl(UninterpFunDeclContext ctx) {\n      ConstructorTypeContext ctc = ctx.constructorType();\n      List<Type> typeArgs = typeExtractor.extract(ctc.typeList().type());\n      Type type = typeExtractor.extract(ctx.type());\n      ConstructorSymbol csym =\n          pc.symbolManager()\n              .createConstructorSymbol(\n                  ctc.ID().getText(),\n                  typeArgs.size(),\n                  ConstructorSymbolType.SOLVER_UNINTERPRETED_FUNCTION,\n                  new FunctorType(typeArgs, type));\n      Set<Type> allTypes = new HashSet<>(typeArgs);\n      allTypes.add(type);\n      for (Type ty : allTypes) {\n        if (!hasSmtType(ty)) {\n          throw new UncheckedParseException(\n              ctx.start.getLine(),\n              \"Uninterpreted function must have an \"\n                  + BuiltInTypeSymbol.SMT_TYPE\n                  + \" type: \"\n                  + csym);\n        }\n        if (!Types.getTypeVars(ty).isEmpty()) {\n          throw new UncheckedParseException(\n              ctx.start.getLine(), \"Uninterpreted functions cannot have type variables: \" + csym);\n        }\n      }\n      uninterpFuncSymbols.add(csym);\n      return null;\n    }\n\n    private boolean hasSmtType(Type type) {\n      return (type instanceof AlgebraicDataType)\n          && ((AlgebraicDataType) type).getSymbol().equals(BuiltInTypeSymbol.SMT_TYPE);\n    }\n\n    @Override\n    public Void visitClauseStmt(ClauseStmtContext ctx) {\n      List<ComplexLiteral> head = termsToLiterals(ctx.clause().head.term());\n      List<ComplexLiteral> body = termsToLiterals(ctx.clause().body.term());\n      Set<BasicRule> newRules = makeRules(ctx.start.getLine(), head, body);\n      for (BasicRule rule : newRules) {\n        RelationSymbol sym = rule.getHead().getSymbol();\n        if (!sym.isIdbSymbol()) {\n          throw new UncheckedParseException(\n              ctx.start.getLine(), \"Cannot define a rule for a non-IDB symbol: \" + sym);\n        }\n        Util.lookupOrCreate(rules, sym, HashSet::new).add(rule);\n      }\n      return null;\n    }\n\n    private Set<BasicRule> makeRules(int lineNo, ComplexLiteral head) {\n      return makeRules(lineNo, Collections.singletonList(head), Collections.emptyList());\n    }\n\n    private Set<BasicRule> makeRules(\n        int lineNo, List<ComplexLiteral> heads, List<ComplexLiteral> body) {\n      List<UserPredicate> processedHeads = new ArrayList<>();\n      for (ComplexLiteral hd : heads) {\n        if (hd instanceof UserPredicate) {\n          processedHeads.add((UserPredicate) hd);\n        } else {\n          throw new UncheckedParseException(\n              lineNo, \"Cannot create rule with non-user predicate in head: \" + hd);\n        }\n      }\n      try {\n        return varChecker.checkRule(processedHeads, body);\n      } catch (VariableCheckPassException e) {\n        throw new UncheckedParseException(lineNo, e.getMessage());\n      }\n    }\n\n    @Override\n    public Void visitFactStmt(FactStmtContext ctx) {\n      ComplexLiteral lit = termToLiteral(ctx.fact().term());\n      if (!(lit instanceof UserPredicate)) {\n        throw new UncheckedParseException(\n            ctx.start.getLine(), \"Facts must be user-defined predicates: \" + ctx.getText());\n      }\n      UserPredicate fact = (UserPredicate) lit;\n      RelationSymbol sym = fact.getSymbol();\n      if (sym.isIdbSymbol()) {\n        Set<BasicRule> rs = makeRules(ctx.start.getLine(), fact);\n        rules.get(sym).addAll(rs);\n      } else {\n        try {\n          Term[] args = varChecker.checkFact(fact.getArgs());\n          initialFacts.get(sym).add(args);\n        } catch (VariableCheckPassException e) {\n          throw new UncheckedParseException(ctx.start.getLine(), e.getMessage());\n        }\n      }\n      return null;\n    }\n\n    @Override\n    public Void visitQueryStmt(QueryStmtContext ctx) {\n      ComplexLiteral a = termToLiteral(ctx.query().term());\n      if (!(a instanceof UserPredicate)) {\n        throw new UncheckedParseException(\n            ctx.start.getLine(), \"Query must be for a user-defined predicate: \" + a);\n      }\n      if (query != null) {\n        throw new UncheckedParseException(\n            ctx.start.getLine(),\n            \"Cannot have multiple queries in the same program: \" + query + \" and \" + a);\n      }\n      UserPredicate q = (UserPredicate) a;\n      try {\n        query = UserPredicate.make(q.getSymbol(), varChecker.checkFact(q.getArgs()), q.isNegated());\n      } catch (VariableCheckPassException e) {\n        throw new UncheckedParseException(\n            ctx.start.getLine(), \"Problem with query \" + query + \": \" + e.getMessage());\n      }\n      return null;\n    }\n\n    List<ComplexLiteral> termsToLiterals(Iterable<TermContext> ctxs) {\n      List<ComplexLiteral> l = new ArrayList<>();\n      for (TermContext ctx : ctxs) {\n        l.add(termToLiteral(ctx));\n      }\n      return l;\n    }\n\n    private ComplexLiteral termToLiteral(TermContext ctx) {\n      Term t = termExtractor.extract(ctx);\n      if (!(t instanceof FunctionCall)) {\n        return ComplexLiterals.unifyWithBool(t, true);\n      }\n      FunctionCall call = (FunctionCall) t;\n      FunctionSymbol sym = call.getSymbol();\n      boolean negated = false;\n      if (sym.equals(BuiltInFunctionSymbol.BNOT)) {\n        t = call.getArgs()[0];\n        if (!(t instanceof FunctionCall)) {\n          return ComplexLiterals.unifyWithBool(t, false);\n        }\n        negated = true;\n        call = (FunctionCall) t;\n        sym = call.getSymbol();\n      }\n      if (sym instanceof PredicateFunctionSymbol) {\n        RelationSymbol predSym = ((PredicateFunctionSymbol) sym).getPredicateSymbol();\n        return UserPredicate.make(predSym, call.getArgs(), negated);\n      }\n      if (!negated && sym.equals(BuiltInFunctionSymbol.BEQ)) {\n        Term[] args = call.getArgs();\n        return UnificationPredicate.make(args[0], args[1], false);\n      }\n      if (!negated && sym.equals(BuiltInFunctionSymbol.BNEQ)) {\n        Term[] args = call.getArgs();\n        return UnificationPredicate.make(args[0], args[1], true);\n      }\n      return ComplexLiterals.unifyWithBool(t, !negated);\n    }\n\n    public BasicProgram program() throws ParseException {\n      return new BasicProgram() {\n\n        @Override\n        public Set<FunctionSymbol> getFunctionSymbols() {\n          return pc.functionDefManager().getFunctionSymbols();\n        }\n\n        @Override\n        public Set<RelationSymbol> getFactSymbols() {\n          return Set.copyOf(initialFacts.keySet());\n        }\n\n        @Override\n        public Set<RelationSymbol> getRuleSymbols() {\n          return Set.copyOf(rules.keySet());\n        }\n\n        @Override\n        public FunctionDef getDef(FunctionSymbol sym) {\n          return pc.functionDefManager().lookup(sym);\n        }\n\n        @Override\n        public Set<Term[]> getFacts(RelationSymbol sym) {\n          if (!sym.isEdbSymbol()) {\n            throw new IllegalArgumentException();\n          }\n          if (!initialFacts.containsKey(sym)) {\n            throw new IllegalArgumentException();\n          }\n          return initialFacts.get(sym);\n        }\n\n        @Override\n        public Set<BasicRule> getRules(RelationSymbol sym) {\n          if (!sym.isIdbSymbol()) {\n            throw new IllegalArgumentException();\n          }\n          if (!rules.containsKey(sym)) {\n            throw new IllegalArgumentException();\n          }\n          return rules.get(sym);\n        }\n\n        @Override\n        public SymbolManager getSymbolManager() {\n          return pc.symbolManager();\n        }\n\n        @Override\n        public boolean hasQuery() {\n          return query != null;\n        }\n\n        @Override\n        public UserPredicate getQuery() {\n          return query;\n        }\n\n        @Override\n        public FunctionCallFactory getFunctionCallFactory() {\n          return pc.functionCallFactory();\n        }\n\n        @Override\n        public Set<ConstructorSymbol> getUninterpretedFunctionSymbols() {\n          return Collections.unmodifiableSet(uninterpFuncSymbols);\n        }\n\n        @Override\n        public Set<TypeSymbol> getTypeSymbols() {\n          return pc.symbolManager().getTypeSymbols();\n        }\n      };\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/parsing/TypeExtractor.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.parsing;\n\nimport static edu.harvard.seas.pl.formulog.util.Util.map;\n\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogBaseVisitor;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.ParenTypeContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.TupleTypeContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.TypeContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.TypeRefContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogParser.TypeVarContext;\nimport edu.harvard.seas.pl.formulog.parsing.generated.FormulogVisitor;\nimport edu.harvard.seas.pl.formulog.symbols.BuiltInTypeSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.GlobalSymbolManager;\nimport edu.harvard.seas.pl.formulog.symbols.Symbol;\nimport edu.harvard.seas.pl.formulog.symbols.TypeSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.parameterized.Param;\nimport edu.harvard.seas.pl.formulog.types.BuiltInTypes;\nimport edu.harvard.seas.pl.formulog.types.Types.AlgebraicDataType;\nimport edu.harvard.seas.pl.formulog.types.Types.Type;\nimport edu.harvard.seas.pl.formulog.types.Types.TypeIndex;\nimport edu.harvard.seas.pl.formulog.types.Types.TypeVar;\nimport java.util.ArrayList;\nimport java.util.List;\n\nclass TypeExtractor {\n\n  private final ParsingContext pc;\n\n  public TypeExtractor(ParsingContext parsingContext) {\n    pc = parsingContext;\n  }\n\n  public Type extract(TypeContext ctx) {\n    try {\n      return ctx.accept(typeExtractor);\n    } catch (UncheckedParseException e) {\n      throw e;\n    } catch (Exception e) {\n      throw new UncheckedParseException(ctx.start.getLine(), e.getMessage());\n    }\n  }\n\n  public List<Type> extract(List<TypeContext> ctxs) {\n    List<Type> l = new ArrayList<>();\n    for (TypeContext ctx : ctxs) {\n      l.add(extract(ctx));\n    }\n    return l;\n  }\n\n  private final FormulogVisitor<Type> typeExtractor =\n      new FormulogBaseVisitor<Type>() {\n\n        @Override\n        public Type visitTupleType(TupleTypeContext ctx) {\n          List<Type> typeArgs = map(ctx.type0(), t -> t.accept(this));\n          if (typeArgs.size() == 1) {\n            return typeArgs.get(0);\n          }\n          TypeSymbol sym = GlobalSymbolManager.lookupTupleTypeSymbol(typeArgs.size());\n          return AlgebraicDataType.make(sym, typeArgs);\n        }\n\n        @Override\n        public Type visitTypeVar(TypeVarContext ctx) {\n          return TypeVar.get(ctx.getText());\n        }\n\n        @Override\n        public Type visitTypeRef(TypeRefContext ctx) {\n          List<Type> typeArgs;\n          if (ctx.type0() != null) {\n            typeArgs = new ArrayList<>();\n            typeArgs.add(ctx.type0().accept(this));\n          } else {\n            typeArgs = map(ctx.type(), t -> t.accept(this));\n          }\n          String s = ctx.ID().getText();\n          List<Param> params = ParsingUtil.extractParams(pc, ctx.parameterList());\n          switch (s) {\n            case \"i32\":\n              if (typeArgs.size() != 0) {\n                throw new UncheckedParseException(\n                    ctx.start.getLine(), \"Built in type i32 does not have any type parameters.\");\n              }\n              return BuiltInTypes.i32;\n            case \"i64\":\n              if (typeArgs.size() != 0) {\n                throw new UncheckedParseException(\n                    ctx.start.getLine(), \"Built in type i64 does not have any type parameters.\");\n              }\n              return BuiltInTypes.i64;\n            case \"fp32\":\n              if (typeArgs.size() != 0) {\n                throw new UncheckedParseException(\n                    ctx.start.getLine(), \"Built in type fp32 does not have any type parameters.\");\n              }\n              return BuiltInTypes.fp32;\n            case \"fp64\":\n              if (typeArgs.size() != 0) {\n                throw new UncheckedParseException(\n                    ctx.start.getLine(), \"Built in type fp64 does not have any type parameters.\");\n              }\n              return BuiltInTypes.fp64;\n            case \"string\":\n              if (typeArgs.size() != 0) {\n                throw new UncheckedParseException(\n                    ctx.start.getLine(), \"Built in type string does not have any type parameters.\");\n              }\n              return BuiltInTypes.string;\n            default:\n              String name = ctx.ID().getText();\n              Symbol sym = pc.symbolManager().lookupSymbol(name);\n              if (!(sym instanceof TypeSymbol)) {\n                throw new UncheckedParseException(ctx.start.getLine(), \"Not a type symbol: \" + sym);\n              }\n              for (Param param : params) {\n                typeArgs.add(param.getType());\n              }\n              if (sym.equals(BuiltInTypeSymbol.FP)\n                  && typeArgs.size() == 1\n                  && typeArgs.get(0) instanceof TypeIndex) {\n                List<TypeIndex> expanded = ((TypeIndex) typeArgs.get(0)).expandAsFpIndex();\n                typeArgs.clear();\n                typeArgs.addAll(expanded);\n              }\n              if (sym.getArity() != typeArgs.size()) {\n                var msg =\n                    \"Type \"\n                        + sym\n                        + \" expects \"\n                        + sym.getArity()\n                        + \" parameter(s), but \"\n                        + typeArgs.size()\n                        + \" provided\";\n                throw new UncheckedParseException(ctx.start.getLine(), msg);\n              }\n              return pc.typeManager().lookup((TypeSymbol) sym, typeArgs);\n          }\n        }\n\n        @Override\n        public Type visitParenType(ParenTypeContext ctx) {\n          return ctx.type().accept(this);\n        }\n      };\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/parsing/UncheckedParseException.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.parsing;\n\npublic class UncheckedParseException extends RuntimeException {\n\n  private static final long serialVersionUID = -4969126551201111362L;\n  private final int lineNo;\n  private final String fileName;\n\n  public UncheckedParseException(int lineNo, String message) {\n    super(message);\n    this.lineNo = lineNo;\n    this.fileName = null;\n  }\n\n  public UncheckedParseException(int lineNo, Throwable cause) {\n    super(cause);\n    this.lineNo = lineNo;\n    this.fileName = null;\n  }\n\n  public UncheckedParseException(ParseException e) {\n    super(e.getMessage());\n    this.lineNo = e.getLineNo();\n    this.fileName = e.getFileName();\n  }\n\n  public int getLineNo() {\n    return lineNo;\n  }\n\n  public String getFileName() {\n    return fileName;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/parsing/VariableCheckPass.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.parsing;\n\nimport edu.harvard.seas.pl.formulog.ast.BasicRule;\nimport edu.harvard.seas.pl.formulog.ast.BindingType;\nimport edu.harvard.seas.pl.formulog.ast.ComplexLiteral;\nimport edu.harvard.seas.pl.formulog.ast.ComplexLiterals.ComplexLiteralVisitor;\nimport edu.harvard.seas.pl.formulog.ast.Constructor;\nimport edu.harvard.seas.pl.formulog.ast.Expr;\nimport edu.harvard.seas.pl.formulog.ast.Exprs.ExprVisitor;\nimport edu.harvard.seas.pl.formulog.ast.Fold;\nimport edu.harvard.seas.pl.formulog.ast.FunctionCallFactory;\nimport edu.harvard.seas.pl.formulog.ast.FunctionCallFactory.FunctionCall;\nimport edu.harvard.seas.pl.formulog.ast.LetFunExpr;\nimport edu.harvard.seas.pl.formulog.ast.MatchClause;\nimport edu.harvard.seas.pl.formulog.ast.MatchExpr;\nimport edu.harvard.seas.pl.formulog.ast.NestedFunctionDef;\nimport edu.harvard.seas.pl.formulog.ast.Primitive;\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.ast.Terms;\nimport edu.harvard.seas.pl.formulog.ast.Terms.TermVisitor;\nimport edu.harvard.seas.pl.formulog.ast.UnificationPredicate;\nimport edu.harvard.seas.pl.formulog.ast.UserPredicate;\nimport edu.harvard.seas.pl.formulog.ast.Var;\nimport edu.harvard.seas.pl.formulog.functions.DummyFunctionDef;\nimport edu.harvard.seas.pl.formulog.functions.FunctionDefManager;\nimport edu.harvard.seas.pl.formulog.symbols.FunctionSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.PredicateFunctionSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.SymbolManager;\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\npublic class VariableCheckPass {\n\n  private final SymbolManager sm;\n\n  public VariableCheckPass(SymbolManager sm) {\n    this.sm = sm;\n  }\n\n  public Term checkFunction(Iterable<Var> arguments, Term body) throws VariableCheckPassException {\n    PassContext ctx = new PassContext(arguments);\n    Term newBody = ctx.checkTerm(body);\n    ctx.checkCounts();\n    return newBody;\n  }\n\n  public Set<BasicRule> checkRule(List<UserPredicate> heads, List<ComplexLiteral> body)\n      throws VariableCheckPassException {\n    PassContext ctx = new PassContext();\n    Set<BasicRule> r = ctx.checkRule(heads, body);\n    try {\n      ctx.checkCounts();\n    } catch (VariableCheckPassException e) {\n      String message = \"Variable usage error in rule: \" + e.getMessage() + \"\\n\\n\";\n      for (int i = 0; i < heads.size(); ++i) {\n        message += heads.get(i);\n        if (i < heads.size() - 1) {\n          message += \",\\n\";\n        }\n      }\n      if (body.size() > 0) {\n        message += \" :-\\n\";\n      }\n      for (int i = 0; i < body.size(); ++i) {\n        message += \"\\t\" + body.get(i);\n        if (i < body.size() - 1) {\n          message += \",\\n\";\n        }\n      }\n      message += \".\";\n      throw new VariableCheckPassException(message);\n    }\n    return r;\n  }\n\n  public Term[] checkFact(Term[] fact) throws VariableCheckPassException {\n    PassContext ctx = new PassContext();\n    Term[] newArgs = new Term[fact.length];\n    for (int i = 0; i < fact.length; ++i) {\n      newArgs[i] = ctx.checkTerm(fact[i]);\n    }\n    try {\n      ctx.checkCounts();\n    } catch (VariableCheckPassException e) {\n      throw new VariableCheckPassException(\"Variable usage error in fact: \" + e.getMessage());\n    }\n    return newArgs;\n  }\n\n  private class PassContext {\n\n    private final Map<Var, Integer> cnts = new HashMap<>();\n    private final Set<Var> fresh = new HashSet<>();\n\n    public PassContext(Iterable<Var> seed) {\n      for (Var x : seed) {\n        int cnt = Util.lookupOrCreate(cnts, x, () -> 0);\n        cnts.put(x, cnt + 1);\n      }\n    }\n\n    public PassContext() {}\n\n    public Set<BasicRule> checkRule(List<UserPredicate> heads, List<ComplexLiteral> body) {\n      List<ComplexLiteral> newBody = new ArrayList<>();\n      for (ComplexLiteral l : body) {\n        newBody.add(checkLiteral(l));\n      }\n      Set<BasicRule> s = new HashSet<>();\n      for (UserPredicate head : heads) {\n        head = (UserPredicate) checkLiteral(head);\n        s.add(BasicRule.make(head, newBody));\n      }\n      return s;\n    }\n\n    public ComplexLiteral checkLiteral(ComplexLiteral l) {\n      Term[] newArgs = Terms.map(l.getArgs(), this::checkTerm);\n      return l.accept(\n          new ComplexLiteralVisitor<Void, ComplexLiteral>() {\n\n            @Override\n            public ComplexLiteral visit(UnificationPredicate pred, Void input) {\n              return UnificationPredicate.make(newArgs[0], newArgs[1], pred.isNegated());\n            }\n\n            @Override\n            public ComplexLiteral visit(UserPredicate pred, Void input) {\n              return UserPredicate.make(pred.getSymbol(), newArgs, pred.isNegated());\n            }\n          },\n          null);\n    }\n\n    public Term checkTerm(Term t) {\n      return t.accept(\n          new TermVisitor<Void, Term>() {\n\n            @Override\n            public Term visit(Var x, Void in) {\n              int cnt = Util.lookupOrCreate(cnts, x, () -> 0);\n              cnts.put(x, cnt + 1);\n              if (looksAnonymous(x)) {\n                x = Var.fresh();\n                fresh.add(x);\n              }\n              return x;\n            }\n\n            @Override\n            public Term visit(Constructor c, Void in) {\n              Term[] newArgs = Terms.map(c.getArgs(), PassContext.this::checkTerm);\n              return c.copyWithNewArgs(newArgs);\n            }\n\n            @Override\n            public Term visit(Primitive<?> p, Void in) {\n              return p;\n            }\n\n            @Override\n            public Term visit(Expr e, Void in) {\n              return checkExpr(e);\n            }\n          },\n          null);\n    }\n\n    public Expr checkExpr(Expr e) {\n      return e.accept(\n          new ExprVisitor<Void, Expr>() {\n\n            @Override\n            public Expr visit(MatchExpr matchExpr, Void in) {\n              Term scrutinee = checkTerm(matchExpr.getMatchee());\n              List<MatchClause> clauses = new ArrayList<>();\n              for (MatchClause cl : matchExpr) {\n                Term lhs = checkTerm(cl.getLhs());\n                Term rhs = checkTerm(cl.getRhs());\n                clauses.add(MatchClause.make(lhs, rhs));\n              }\n              return MatchExpr.make(scrutinee, clauses);\n            }\n\n            @Override\n            public Expr visit(FunctionCall funcCall, Void in) {\n              Term[] newArgs = Terms.map(funcCall.getArgs(), PassContext.this::checkTerm);\n              FunctionCallFactory factory = funcCall.getFactory();\n              FunctionSymbol sym = funcCall.getSymbol();\n              if (sym instanceof PredicateFunctionSymbol) {\n                Pair<PredicateFunctionSymbol, Term[]> p =\n                    updatePlaceholder((PredicateFunctionSymbol) sym, newArgs);\n                sym = p.fst();\n                newArgs = p.snd();\n                FunctionDefManager dm = factory.getDefManager();\n                if (!dm.hasDefinition(sym)) {\n                  dm.register(new DummyFunctionDef(sym));\n                }\n              }\n              return factory.make(sym, newArgs);\n            }\n\n            @Override\n            public Expr visit(LetFunExpr letFun, Void in) {\n              Set<NestedFunctionDef> defs = new HashSet<>();\n              for (NestedFunctionDef funcDef : letFun.getDefs()) {\n                List<Var> newParams = new ArrayList<>();\n                for (Var x : funcDef.getParams()) {\n                  newParams.add((Var) checkTerm(x));\n                }\n                Term newBody = checkTerm(funcDef.getBody());\n                defs.add(NestedFunctionDef.make(funcDef.getSymbol(), newParams, newBody));\n              }\n              return LetFunExpr.make(defs, checkTerm(letFun.getLetBody()));\n            }\n\n            @Override\n            public Expr visit(Fold fold, Void in) {\n              FunctionCall f = (FunctionCall) fold.getShamCall().accept(this, in);\n              return Fold.mk(f.getSymbol(), f.getArgs(), f.getFactory());\n            }\n          },\n          null);\n    }\n\n    private Pair<PredicateFunctionSymbol, Term[]> updatePlaceholder(\n        PredicateFunctionSymbol placeholder, Term[] args) {\n      BindingType[] bindings = new BindingType[args.length];\n      List<Term> argsToKeep = new ArrayList<>();\n      for (int i = 0; i < args.length; ++i) {\n        Term arg = args[i];\n        if (arg instanceof Var) {\n          Var x = (Var) arg;\n          if (fresh.contains(x)) {\n            bindings[i] = BindingType.IGNORED;\n          } else if (looksLikeHole(x)) {\n            bindings[i] = BindingType.FREE;\n            cnts.put(x, cnts.get(x) - 1);\n          } else {\n            bindings[i] = BindingType.BOUND;\n            argsToKeep.add(arg);\n          }\n        } else {\n          bindings[i] = BindingType.BOUND;\n          argsToKeep.add(arg);\n        }\n      }\n      PredicateFunctionSymbol sym =\n          sm.createPredicateFunctionSymbol(placeholder.getPredicateSymbol(), bindings);\n      args = argsToKeep.toArray(Terms.emptyArray());\n      return new Pair<>(sym, args);\n    }\n\n    public void checkCounts() throws VariableCheckPassException {\n      for (Map.Entry<Var, Integer> e : cnts.entrySet()) {\n        Var x = e.getKey();\n        int cnt = e.getValue();\n        if (looksLikeHole(x) && cnt > 0) {\n          throw new VariableCheckPassException(\n              \"Can only use hole ?? as an argument to a predicate aggregate function.\");\n        } else if (looksLikeQuasiAnonymousVar(x) && cnt > 1) {\n          throw new VariableCheckPassException(\n              \"Quasi-anonymous variable \" + x + \" occurs more than once.\");\n        } else if (!looksAnonymous(x) && cnt == 1) {\n          throw new VariableCheckPassException(\"Named variable \" + x + \" only occurs once.\");\n        }\n      }\n    }\n  }\n\n  private static boolean looksLikeHole(Var x) {\n    return x.equals(Var.makeHole());\n  }\n\n  private static boolean looksAnonymous(Var x) {\n    return x.toString().startsWith(\"_\") || x.toString().startsWith(\"$\");\n  }\n\n  private static boolean looksLikeTrueAnonymousVar(Var x) {\n    return x.isUnderscore() || x.toString().startsWith(\"$\");\n  }\n\n  private static boolean looksLikeQuasiAnonymousVar(Var x) {\n    return looksAnonymous(x) && !looksLikeTrueAnonymousVar(x);\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/parsing/VariableCheckPassException.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.parsing;\n\npublic class VariableCheckPassException extends Exception {\n\n  private static final long serialVersionUID = -4879050025193100459L;\n\n  public VariableCheckPassException(String message) {\n    super(message);\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/smt/AbstractSmtLibSolver.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2020-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.smt;\n\nimport edu.harvard.seas.pl.formulog.Configuration;\nimport edu.harvard.seas.pl.formulog.ast.Constructors.SolverVariable;\nimport edu.harvard.seas.pl.formulog.ast.Model;\nimport edu.harvard.seas.pl.formulog.ast.Program;\nimport edu.harvard.seas.pl.formulog.ast.SmtLibTerm;\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport java.io.BufferedReader;\nimport java.io.File;\nimport java.io.FileWriter;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.io.PrintWriter;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.atomic.AtomicInteger;\n\npublic abstract class AbstractSmtLibSolver implements SmtLibSolver {\n\n  protected static final Pair<Collection<SolverVariable>, Collection<SolverVariable>>\n      emptyCollectionPair = new Pair<>(Collections.emptyList(), Collections.emptyList());\n\n  private static final ExternalSolverProcessFactory solverFactory;\n\n  static {\n    switch (Configuration.smtSolver) {\n      case \"z3\":\n        solverFactory = Z3ProcessFactory.get();\n        break;\n      case \"cvc4\":\n        solverFactory = Cvc4ProcessFactory.get();\n        break;\n      case \"yices\":\n        solverFactory = YicesProcessFactory.get();\n        break;\n      case \"boolector\":\n        solverFactory = BoolectorProcessFactory.get();\n        break;\n      default:\n        throw new AssertionError(\"impossible\");\n    }\n  }\n\n  private static final AtomicInteger solverCnt = new AtomicInteger();\n\n  protected final int solverId = solverCnt.getAndIncrement();\n\n  protected SmtLibShim shim;\n  protected Process solver;\n  private final PrintWriter log;\n\n  protected static int taskCnt;\n\n  public AbstractSmtLibSolver() {\n    PrintWriter w = null;\n    if (Configuration.debugSmt) {\n      try {\n        Path path = Files.createDirectories(Paths.get(Configuration.debugSmtOutDir));\n        File log = path.resolve(\"solver\" + solverId + \".log.smt2\").toFile();\n        w = new PrintWriter(new FileWriter(log));\n      } catch (IOException e) {\n        System.err.println(\"WARNING: Unable to create log for solver #\" + solverId);\n      }\n    }\n    log = w;\n    instances.add(this);\n  }\n\n  protected abstract boolean isIncremental();\n\n  @Override\n  public synchronized void start(Program<?, ?> prog) throws EvaluationException {\n    assert solver == null;\n    try {\n      solver = solverFactory.newProcess(isIncremental());\n    } catch (IOException e) {\n      throw new AssertionError(\"Could not create external solver process:\\n\" + e);\n    }\n    BufferedReader reader = new BufferedReader(new InputStreamReader(solver.getInputStream()));\n    PrintWriter writer = new PrintWriter(solver.getOutputStream());\n    shim = new SmtLibShim(reader, writer, log);\n    shim.initialize(prog, Configuration.smtDeclareAdts);\n    start();\n  }\n\n  @Override\n  public synchronized void destroy() {\n    while (solver != null && solver.isAlive()) {\n      try {\n        solver.getOutputStream().close();\n        solver.waitFor();\n        solver = null;\n      } catch (Exception e) {\n        e.printStackTrace();\n      }\n    }\n  }\n\n  private static final Set<AbstractSmtLibSolver> instances = Util.concurrentSet();\n\n  public static synchronized void destroyAll() {\n    for (var solver : instances) {\n      solver.destroy();\n    }\n  }\n\n  @Override\n  public void finalize() {\n    instances.remove(this);\n    destroy();\n  }\n\n  protected abstract void start() throws EvaluationException;\n\n  protected abstract Pair<Collection<SolverVariable>, Collection<SolverVariable>> makeAssertions(\n      Collection<SmtLibTerm> assertions) throws EvaluationException;\n\n  protected abstract void cleanup() throws EvaluationException;\n\n  private SmtResult makeResult(SmtStatus status, Map<SolverVariable, Term> m, int taskId) {\n    Model model = m == null ? null : Model.make(m);\n    return new SmtResult(status, model, solverId, taskId);\n  }\n\n  @Override\n  public synchronized SmtResult check(\n      Collection<SmtLibTerm> assertions, boolean getModel, int timeout) throws EvaluationException {\n    assert solver != null;\n    int taskId = taskCnt++;\n    if (assertions.isEmpty()) {\n      Map<SolverVariable, Term> m = getModel ? Collections.emptyMap() : null;\n      return makeResult(SmtStatus.SATISFIABLE, m, taskId);\n    }\n    String taskName =\n        \"#\" + solverId + \":\" + taskId + \" (thread #\" + Thread.currentThread().getId() + \")\";\n    shim.printComment(\"*** START CALL \" + taskName + \" ***\");\n    boolean debug = Configuration.timeSmt || log != null;\n    long start = 0;\n    if (debug) {\n      start = System.nanoTime();\n    }\n    Pair<Collection<SolverVariable>, Collection<SolverVariable>> p = makeAssertions(assertions);\n    long encodeTime = 0;\n    if (debug) {\n      long end = System.nanoTime();\n      encodeTime = end - start;\n      start = end;\n    }\n    try {\n      SmtStatus status = shim.checkSatAssuming(p.fst(), p.snd(), timeout);\n      if (debug) {\n        long evalTime = System.nanoTime() - start;\n        Configuration.recordSmtEvalTime(this, encodeTime, evalTime, status);\n        if (log != null) {\n          log.println(\"; time: \" + evalTime / 1e6 + \"ms\");\n          log.flush();\n        }\n      }\n      Map<SolverVariable, Term> m = null;\n      if (status.equals(SmtStatus.SATISFIABLE) && getModel) {\n        m = shim.getModel();\n      }\n      cleanup();\n      shim.printComment(\"*** END CALL \" + taskName + \" ***\\n\");\n      return makeResult(status, m, taskId);\n    } catch (EvaluationException e) {\n      throw new EvaluationException(\"Problem with solver \" + solverId + \":\\n\" + e.getMessage());\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/smt/BestMatchSmtManager.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.smt;\n\nimport edu.harvard.seas.pl.formulog.Configuration;\nimport edu.harvard.seas.pl.formulog.ast.Program;\nimport edu.harvard.seas.pl.formulog.ast.SmtLibTerm;\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport java.util.Collection;\nimport java.util.Comparator;\nimport java.util.PriorityQueue;\nimport java.util.Set;\nimport java.util.concurrent.atomic.AtomicIntegerArray;\n\npublic class BestMatchSmtManager implements SmtLibSolver {\n\n  private final CheckSatAssumingSolver[] solvers;\n  private final AtomicIntegerArray statuses;\n  private static final int cacheCap = Configuration.smtCacheSize;\n\n  public BestMatchSmtManager(int size) {\n    solvers = new CheckSatAssumingSolver[size];\n    statuses = new AtomicIntegerArray(size);\n  }\n\n  @Override\n  public SmtResult check(Collection<SmtLibTerm> conjuncts, boolean getModel, int timeout)\n      throws EvaluationException {\n    while (true) {\n      PriorityQueue<Pair<Integer, Double>> q = new PriorityQueue<>(solvers.length, cmp);\n      for (int i = 0; i < solvers.length; ++i) {\n        double score = score(conjuncts, solvers[i]);\n        q.add(new Pair<>(i, score));\n      }\n      while (!q.isEmpty()) {\n        int i = q.remove().fst();\n        if (statuses.compareAndSet(i, 0, 1)) {\n          try {\n            return solvers[i].check(conjuncts, getModel, timeout);\n          } finally {\n            statuses.set(i, 0);\n          }\n        }\n      }\n    }\n  }\n\n  private static final Comparator<Pair<Integer, Double>> cmp =\n      new Comparator<Pair<Integer, Double>>() {\n\n        @Override\n        public int compare(Pair<Integer, Double> o1, Pair<Integer, Double> o2) {\n          return Double.compare(o2.snd(), o1.snd());\n        }\n      };\n\n  private double score(Collection<SmtLibTerm> conjuncts, CheckSatAssumingSolver solver) {\n    Set<SmtLibTerm> cache = solver.getCache();\n    int cacheSize = cache.size();\n    if (cacheSize == 0) {\n      return 0;\n    }\n    int hits = 0;\n    for (SmtLibTerm conjunct : conjuncts) {\n      if (cache.contains(conjunct)) {\n        hits++;\n      }\n    }\n    double score1 = 3 * hits / conjuncts.size();\n    double score2 = -((cacheSize - hits) / cacheCap);\n    return score1 + score2;\n  }\n\n  @Override\n  public void start(Program<?, ?> prog) throws EvaluationException {\n    for (int i = 0; i < solvers.length; ++i) {\n      CheckSatAssumingSolver solver = new CheckSatAssumingSolver();\n      solver.start(prog);\n      solvers[i] = solver;\n    }\n  }\n\n  @Override\n  public void destroy() {\n    for (SmtLibSolver solver : solvers) {\n      solver.destroy();\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/smt/BoolectorProcessFactory.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2020-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.smt;\n\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class BoolectorProcessFactory implements ExternalSolverProcessFactory {\n\n  private static BoolectorProcessFactory instance;\n\n  private BoolectorProcessFactory() {\n    Util.assertBinaryOnPath(\"boolector\");\n  }\n\n  public static BoolectorProcessFactory get() {\n    if (instance == null) {\n      synchronized (BoolectorProcessFactory.class) {\n        if (instance == null) {\n          instance = new BoolectorProcessFactory();\n        }\n      }\n    }\n    return instance;\n  }\n\n  @Override\n  public Process newProcess(boolean incremental) throws IOException {\n    List<String> command = new ArrayList<>();\n    command.add(\"boolector\");\n    command.add(\"--smt2\");\n    if (incremental) {\n      command.add(\"--incremental\");\n    }\n    return new ProcessBuilder(command).redirectErrorStream(true).start();\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/smt/CallAndResetSolver.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2020-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.smt;\n\nimport edu.harvard.seas.pl.formulog.Configuration;\nimport edu.harvard.seas.pl.formulog.Main;\nimport edu.harvard.seas.pl.formulog.ast.Constructors.SolverVariable;\nimport edu.harvard.seas.pl.formulog.ast.SmtLibTerm;\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport java.util.Collection;\n\npublic class CallAndResetSolver extends AbstractSmtLibSolver {\n\n  @Override\n  protected Pair<Collection<SolverVariable>, Collection<SolverVariable>> makeAssertions(\n      Collection<SmtLibTerm> assertions) throws EvaluationException {\n    for (SmtLibTerm assertion : assertions) {\n      shim.makeAssertion(assertion);\n    }\n    if (Main.smtStats) {\n      Configuration.smtCacheMisses.add(assertions.size());\n      Configuration.smtCacheClears.increment();\n    }\n    return emptyCollectionPair;\n  }\n\n  @Override\n  protected void cleanup() throws EvaluationException {\n    shim.reset();\n    shim.makeDeclarations();\n  }\n\n  @Override\n  protected void start() throws EvaluationException {\n    shim.setLogic(Configuration.smtLogic);\n    shim.makeDeclarations();\n  }\n\n  @Override\n  protected boolean isIncremental() {\n    return false;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/smt/CheckSatAssumingSolver.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.smt;\n\nimport edu.harvard.seas.pl.formulog.Configuration;\nimport edu.harvard.seas.pl.formulog.Main;\nimport edu.harvard.seas.pl.formulog.ast.Constructors;\nimport edu.harvard.seas.pl.formulog.ast.Constructors.SolverVariable;\nimport edu.harvard.seas.pl.formulog.ast.SmtLibTerm;\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.ast.Terms;\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport edu.harvard.seas.pl.formulog.symbols.BuiltInConstructorSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.GlobalSymbolManager;\nimport edu.harvard.seas.pl.formulog.symbols.parameterized.BuiltInConstructorSymbolBase;\nimport edu.harvard.seas.pl.formulog.symbols.parameterized.Param;\nimport edu.harvard.seas.pl.formulog.symbols.parameterized.ParamKind;\nimport edu.harvard.seas.pl.formulog.symbols.parameterized.ParameterizedConstructorSymbol;\nimport edu.harvard.seas.pl.formulog.types.BuiltInTypes;\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\npublic class CheckSatAssumingSolver extends AbstractSmtLibSolver {\n\n  private final Map<SmtLibTerm, SolverVariable> indicatorVars = new HashMap<>();\n  private int nextVarId;\n\n  private void clearCache() throws EvaluationException {\n    if (Configuration.timeSmt) {\n      Configuration.recordCsaCacheClear(solverId);\n    }\n    if (Main.smtStats) {\n      Configuration.smtCacheClears.increment();\n    }\n    indicatorVars.clear();\n    nextVarId = 0;\n    if (Configuration.smtCacheHardResets) {\n      shim.reset();\n      start();\n    } else {\n      shim.pop();\n      shim.push();\n    }\n  }\n\n  public Set<SmtLibTerm> getCache() {\n    return indicatorVars.keySet();\n  }\n\n  @Override\n  protected Pair<Collection<SolverVariable>, Collection<SolverVariable>> makeAssertions(\n      Collection<SmtLibTerm> formula) throws EvaluationException {\n    int oldSize = indicatorVars.size();\n    int hits = 0;\n    int misses = 0;\n    Set<SolverVariable> onVars = new HashSet<>();\n    for (SmtLibTerm conjunct : formula) {\n      SolverVariable x = indicatorVars.get(conjunct);\n      if (x != null) {\n        hits++;\n      } else {\n        misses++;\n        x = makeIndicatorVar(conjunct);\n        indicatorVars.put(conjunct, x);\n        SmtLibTerm imp = makeImp(x, conjunct);\n        shim.makeAssertion(imp);\n      }\n      onVars.add(x);\n    }\n    if (Configuration.timeSmt) {\n      Configuration.recordCsaCacheStats(solverId, hits, misses, oldSize);\n    }\n    if (Main.smtStats) {\n      Configuration.smtCacheHits.add(hits);\n      Configuration.smtCacheMisses.add(misses);\n    }\n    Collection<SolverVariable> offVars;\n    if (Configuration.smtUseNegativeLiterals) {\n      offVars =\n          indicatorVars.values().stream()\n              .filter(x -> !onVars.contains(x))\n              .collect(Collectors.toList());\n    } else {\n      offVars = Collections.emptySet();\n    }\n    return new Pair<>(onVars, offVars);\n  }\n\n  private SmtLibTerm makeImp(SolverVariable x, SmtLibTerm assertion) {\n    Term[] args = {x, assertion};\n    return Constructors.make(BuiltInConstructorSymbol.SMT_IMP, args);\n  }\n\n  private SolverVariable makeIndicatorVar(SmtLibTerm assertion) {\n    Term[] args = Terms.singletonArray(Terms.makeDummyTerm(nextVarId++));\n    ParameterizedConstructorSymbol sym =\n        GlobalSymbolManager.getParameterizedSymbol(BuiltInConstructorSymbolBase.SMT_VAR);\n    sym =\n        sym.copyWithNewArgs(Param.wildCard(), new Param(BuiltInTypes.bool, ParamKind.PRE_SMT_TYPE));\n    return (SolverVariable) Constructors.make(sym, args);\n  }\n\n  @Override\n  protected void cleanup() throws EvaluationException {\n    if (indicatorVars.size() > Configuration.smtCacheSize) {\n      clearCache();\n    }\n  }\n\n  @Override\n  protected void start() throws EvaluationException {\n    shim.setLogic(Configuration.smtLogic);\n    shim.makeDeclarations();\n    if (!Configuration.smtCacheHardResets) {\n      shim.push();\n    }\n  }\n\n  @Override\n  protected boolean isIncremental() {\n    return true;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/smt/Cvc4ProcessFactory.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2020-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.smt;\n\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class Cvc4ProcessFactory implements ExternalSolverProcessFactory {\n\n  private static Cvc4ProcessFactory instance;\n\n  private Cvc4ProcessFactory() {\n    Util.assertBinaryOnPath(\"cvc4\");\n  }\n\n  public static Cvc4ProcessFactory get() {\n    if (instance == null) {\n      synchronized (Cvc4ProcessFactory.class) {\n        if (instance == null) {\n          instance = new Cvc4ProcessFactory();\n        }\n      }\n    }\n    return instance;\n  }\n\n  @Override\n  public Process newProcess(boolean incremental) throws IOException {\n    List<String> command = new ArrayList<>();\n    command.add(\"cvc4\");\n    command.add(\"--lang\");\n    command.add(\"smt\");\n    if (incremental) {\n      command.add(\"--incremental\");\n    }\n    return new ProcessBuilder(command).redirectErrorStream(true).start();\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/smt/DoubleCheckingSolver.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.smt;\n\nimport edu.harvard.seas.pl.formulog.Configuration;\nimport edu.harvard.seas.pl.formulog.ast.Program;\nimport edu.harvard.seas.pl.formulog.ast.SmtLibTerm;\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport java.util.Collection;\n\npublic class DoubleCheckingSolver implements SmtLibSolver {\n\n  private final SmtLibSolver inner;\n  private final SmtLibSolver checker = new PushPopSolver();\n\n  public DoubleCheckingSolver(SmtLibSolver inner) {\n    this.inner = inner;\n  }\n\n  @Override\n  public void start(Program<?, ?> prog) throws EvaluationException {\n    inner.start(prog);\n    checker.start(prog);\n  }\n\n  @Override\n  public SmtResult check(Collection<SmtLibTerm> formula, boolean getModel, int timeout)\n      throws EvaluationException {\n    SmtResult res = inner.check(formula, getModel, timeout);\n    if (res.status.equals(SmtStatus.UNKNOWN)) {\n      SmtResult res2 = checker.check(formula, getModel, timeout);\n      if (Configuration.timeSmt) {\n        Configuration.recordSmtDoubleCheck(!res2.status.equals(SmtStatus.UNKNOWN));\n      }\n      res = res2;\n    }\n    return res;\n  }\n\n  @Override\n  public void destroy() {\n    inner.destroy();\n    checker.destroy();\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/smt/ExternalSolverProcessFactory.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.smt;\n\nimport java.io.IOException;\n\npublic interface ExternalSolverProcessFactory {\n\n  Process newProcess(boolean incremental) throws IOException;\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/smt/NotThreadSafeQueueSmtManager.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.smt;\n\nimport edu.harvard.seas.pl.formulog.ast.Program;\nimport edu.harvard.seas.pl.formulog.ast.SmtLibTerm;\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport java.util.Collection;\nimport java.util.function.Supplier;\n\npublic class NotThreadSafeQueueSmtManager implements SmtLibSolver {\n\n  private final SmtLibSolver[] solvers;\n  private int pos;\n  private final Supplier<SmtLibSolver> maker;\n\n  public NotThreadSafeQueueSmtManager(int size, Supplier<SmtLibSolver> maker) {\n    if (size <= 0) {\n      throw new IllegalArgumentException(\"Cannot have non-positive number of solvers.\");\n    }\n    solvers = new SmtLibSolver[size];\n    this.maker = maker;\n  }\n\n  @Override\n  public SmtResult check(Collection<SmtLibTerm> conjuncts, boolean getModel, int timeout)\n      throws EvaluationException {\n    SmtLibSolver solver = solvers[pos];\n    SmtResult res = solver.check(conjuncts, getModel, timeout);\n    pos = (pos + 1) % solvers.length;\n    return res;\n  }\n\n  @Override\n  public void start(Program<?, ?> prog) throws EvaluationException {\n    for (int i = 0; i < solvers.length; ++i) {\n      SmtLibSolver solver = maker.get();\n      solver.start(prog);\n      solvers[i] = solver;\n    }\n  }\n\n  @Override\n  public void destroy() {\n    for (SmtLibSolver solver : solvers) {\n      solver.destroy();\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/smt/PerThreadSmtManager.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.smt;\n\nimport edu.harvard.seas.pl.formulog.ast.Program;\nimport edu.harvard.seas.pl.formulog.ast.SmtLibTerm;\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport edu.harvard.seas.pl.formulog.eval.UncheckedEvaluationException;\nimport java.util.Collection;\nimport java.util.function.Supplier;\n\npublic class PerThreadSmtManager implements SmtLibSolver {\n\n  private final ThreadLocal<SmtLibSolver> subManager;\n  private volatile Program<?, ?> prog;\n\n  public PerThreadSmtManager(Supplier<SmtLibSolver> managerMaker) {\n    subManager =\n        new ThreadLocal<SmtLibSolver>() {\n\n          @Override\n          protected SmtLibSolver initialValue() {\n            SmtLibSolver m = managerMaker.get();\n            try {\n              m.start(prog);\n            } catch (EvaluationException e) {\n              throw new UncheckedEvaluationException(e.getMessage());\n            }\n            return m;\n          }\n        };\n  }\n\n  @Override\n  public SmtResult check(Collection<SmtLibTerm> conjuncts, boolean getModel, int timeout)\n      throws EvaluationException {\n    try {\n      return subManager.get().check(conjuncts, getModel, timeout);\n    } catch (UncheckedEvaluationException e) {\n      throw new EvaluationException(e.getMessage());\n    }\n  }\n\n  @Override\n  public void start(Program<?, ?> prog) throws EvaluationException {\n    this.prog = prog;\n  }\n\n  @Override\n  public void destroy() {\n    subManager.get().destroy();\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/smt/PushPopNaiveSolver.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2020-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.smt;\n\nimport edu.harvard.seas.pl.formulog.Configuration;\nimport edu.harvard.seas.pl.formulog.Main;\nimport edu.harvard.seas.pl.formulog.ast.Constructors.SolverVariable;\nimport edu.harvard.seas.pl.formulog.ast.SmtLibTerm;\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport java.util.Collection;\n\npublic class PushPopNaiveSolver extends AbstractSmtLibSolver {\n\n  @Override\n  protected boolean isIncremental() {\n    return true;\n  }\n\n  @Override\n  protected void start() throws EvaluationException {\n    shim.setLogic(Configuration.smtLogic);\n    shim.makeDeclarations();\n  }\n\n  @Override\n  protected Pair<Collection<SolverVariable>, Collection<SolverVariable>> makeAssertions(\n      Collection<SmtLibTerm> assertions) throws EvaluationException {\n    shim.push();\n    for (SmtLibTerm assertion : assertions) {\n      shim.makeAssertion(assertion);\n    }\n    if (Main.smtStats) {\n      Configuration.smtCacheMisses.add(assertions.size());\n      Configuration.smtCacheClears.increment();\n    }\n    return emptyCollectionPair;\n  }\n\n  @Override\n  protected void cleanup() throws EvaluationException {\n    shim.pop();\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/smt/PushPopSolver.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2020-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.smt;\n\nimport edu.harvard.seas.pl.formulog.Configuration;\nimport edu.harvard.seas.pl.formulog.Main;\nimport edu.harvard.seas.pl.formulog.ast.Constructors.SolverVariable;\nimport edu.harvard.seas.pl.formulog.ast.SmtLibTerm;\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport java.util.ArrayDeque;\nimport java.util.Collection;\nimport java.util.Deque;\nimport java.util.Iterator;\n\npublic class PushPopSolver extends AbstractSmtLibSolver {\n\n  private final Deque<SmtLibTerm> cache = new ArrayDeque<>();\n\n  @Override\n  protected Pair<Collection<SolverVariable>, Collection<SolverVariable>> makeAssertions(\n      Collection<SmtLibTerm> assertions) throws EvaluationException {\n    int baseSize = cache.size();\n    int i = findDiffPos(assertions);\n    if (Main.smtStats) {\n      Configuration.smtCacheHits.add(i);\n      if (i == 0 && !cache.isEmpty()) {\n        Configuration.smtCacheClears.increment();\n      }\n      Configuration.smtCacheMisses.add(assertions.size() - i);\n    }\n    int pops = baseSize - i;\n    shrinkCache(i);\n    Iterator<SmtLibTerm> it = assertions.iterator();\n    for (int j = 0; j < i; ++j) {\n      it.next();\n    }\n    growCache(it);\n    if (Configuration.timeSmt) {\n      Configuration.recordPushPopSolverStats(solverId, baseSize, pops, cache.size() - i);\n    }\n    return emptyCollectionPair;\n  }\n\n  private int findDiffPos(Collection<SmtLibTerm> assertions) {\n    int i = 0;\n    Iterator<SmtLibTerm> cacheIt = cache.iterator();\n    for (SmtLibTerm assertion : assertions) {\n      if (!cacheIt.hasNext()) {\n        break;\n      }\n      SmtLibTerm cached = cacheIt.next();\n      if (!cached.equals(assertion)) {\n        break;\n      }\n      ++i;\n    }\n    return i;\n  }\n\n  private void shrinkCache(int tgtSize) throws EvaluationException {\n    int size = cache.size();\n    shim.pop(size - tgtSize);\n    while (size > tgtSize) {\n      cache.removeLast();\n      --size;\n    }\n  }\n\n  private void growCache(Iterator<SmtLibTerm> assertions) throws EvaluationException {\n    while (assertions.hasNext()) {\n      SmtLibTerm assertion = assertions.next();\n      shim.push();\n      shim.makeAssertion(assertion);\n      cache.addLast(assertion);\n    }\n  }\n\n  @Override\n  protected void cleanup() {\n    // Do nothing\n  }\n\n  @Override\n  protected void start() throws EvaluationException {\n    shim.setLogic(Configuration.smtLogic);\n    shim.makeDeclarations();\n  }\n\n  @Override\n  protected boolean isIncremental() {\n    return true;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/smt/QueueSmtManager.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.smt;\n\nimport edu.harvard.seas.pl.formulog.ast.Program;\nimport edu.harvard.seas.pl.formulog.ast.SmtLibTerm;\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport java.util.Collection;\nimport java.util.concurrent.ArrayBlockingQueue;\nimport java.util.function.Supplier;\n\npublic class QueueSmtManager implements SmtLibSolver {\n\n  private final ArrayBlockingQueue<SmtLibSolver> solvers;\n  private final Supplier<SmtLibSolver> maker;\n\n  public QueueSmtManager(int size, Supplier<SmtLibSolver> maker) {\n    if (size <= 0) {\n      throw new IllegalArgumentException(\"Cannot have non-positive number of solvers.\");\n    }\n    solvers = new ArrayBlockingQueue<>(size);\n    this.maker = maker;\n  }\n\n  @Override\n  public SmtResult check(Collection<SmtLibTerm> conjuncts, boolean getModel, int timeout)\n      throws EvaluationException {\n    SmtLibSolver solver;\n    try {\n      solver = solvers.take();\n    } catch (InterruptedException e) {\n      throw new EvaluationException(e);\n    }\n    SmtResult res = solver.check(conjuncts, getModel, timeout);\n    solvers.add(solver);\n    return res;\n  }\n\n  @Override\n  public void start(Program<?, ?> prog) throws EvaluationException {\n    while (solvers.remainingCapacity() > 0) {\n      SmtLibSolver solver = maker.get();\n      solver.start(prog);\n      solvers.add(solver);\n    }\n  }\n\n  @Override\n  public void destroy() {\n    for (SmtLibSolver solver : solvers) {\n      solver.destroy();\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/smt/SingleShotSolver.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2020-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.smt;\n\nimport edu.harvard.seas.pl.formulog.Configuration;\nimport edu.harvard.seas.pl.formulog.Main;\nimport edu.harvard.seas.pl.formulog.ast.Constructors.SolverVariable;\nimport edu.harvard.seas.pl.formulog.ast.Program;\nimport edu.harvard.seas.pl.formulog.ast.SmtLibTerm;\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport java.util.Collection;\n\npublic class SingleShotSolver extends AbstractSmtLibSolver {\n\n  private Program<?, ?> prog;\n\n  @Override\n  protected Pair<Collection<SolverVariable>, Collection<SolverVariable>> makeAssertions(\n      Collection<SmtLibTerm> assertions) throws EvaluationException {\n    shim.setLogic(Configuration.smtLogic);\n    shim.makeDeclarations();\n    for (SmtLibTerm assertion : assertions) {\n      shim.makeAssertion(assertion);\n    }\n    if (Main.smtStats) {\n      Configuration.smtCacheMisses.add(assertions.size());\n      Configuration.smtCacheClears.increment();\n    }\n    return emptyCollectionPair;\n  }\n\n  @Override\n  protected void start() throws EvaluationException {\n    // do nothing\n  }\n\n  @Override\n  protected void cleanup() throws EvaluationException {\n    destroy();\n    super.start(prog);\n  }\n\n  public synchronized void start(Program<?, ?> prog) throws EvaluationException {\n    this.prog = prog;\n    super.start(prog);\n  }\n\n  @Override\n  protected boolean isIncremental() {\n    return false;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/smt/SmtLibParser.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.smt;\n\nimport edu.harvard.seas.pl.formulog.ast.BoolTerm;\nimport edu.harvard.seas.pl.formulog.ast.Constructors;\nimport edu.harvard.seas.pl.formulog.ast.Constructors.SolverVariable;\nimport edu.harvard.seas.pl.formulog.ast.FP32;\nimport edu.harvard.seas.pl.formulog.ast.FP64;\nimport edu.harvard.seas.pl.formulog.ast.I32;\nimport edu.harvard.seas.pl.formulog.ast.I64;\nimport edu.harvard.seas.pl.formulog.ast.StringTerm;\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.ast.Terms;\nimport edu.harvard.seas.pl.formulog.symbols.BuiltInTypeSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.ConstructorSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.Symbol;\nimport edu.harvard.seas.pl.formulog.symbols.SymbolManager;\nimport edu.harvard.seas.pl.formulog.symbols.TypeSymbol;\nimport edu.harvard.seas.pl.formulog.types.FunctorType;\nimport edu.harvard.seas.pl.formulog.types.Types.AlgebraicDataType;\nimport edu.harvard.seas.pl.formulog.types.Types.AlgebraicDataType.ConstructorScheme;\nimport edu.harvard.seas.pl.formulog.types.Types.Type;\nimport edu.harvard.seas.pl.formulog.types.Types.TypeIndex;\nimport java.io.IOException;\nimport java.io.Reader;\nimport java.io.StreamTokenizer;\nimport java.io.StringReader;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\npublic class SmtLibParser {\n\n  private final SymbolManager symbolManager;\n  private final Map<String, SolverVariable> variables;\n\n  public SmtLibParser(SymbolManager symbolManager, Map<String, SolverVariable> variables) {\n    this.symbolManager = symbolManager;\n    this.variables = variables;\n  }\n\n  public Map<SolverVariable, Term> getModel(Reader r) throws IOException, SmtLibParseException {\n    Tokenizer t = new Tokenizer(r);\n    t.consume(\"(\");\n    Map<SolverVariable, Term> m = new HashMap<>();\n    while (!t.peek().equals(\")\")) {\n      if (t.peek().equals(\";\")) {\n        consumeComment(t);\n      } else {\n        parseFunctionDef(m, t);\n      }\n    }\n    t.consume(\")\");\n    t.ignoreWhitespace(false);\n    // Remove EOL\n    t.next();\n    return m;\n  }\n\n  private void consumeComment(Tokenizer t) throws IOException, SmtLibParseException {\n    t.consume(\";;\");\n    t.ignoreWhitespace(false);\n    while (!t.next().equals(\"\\n\")) {\n      // do nothing\n    }\n    t.ignoreWhitespace(true);\n  }\n\n  private void parseFunctionDef(Map<SolverVariable, Term> m, Tokenizer t)\n      throws IOException, SmtLibParseException {\n    t.consume(\"(\");\n    if (t.peek().equals(\"forall\") || t.peek().equals(\"declare\")) {\n      skipRestOfSExp(t);\n      return;\n    }\n    t.consume(\"define-fun\");\n    if (t.peek().equals(\"-\")) {\n      skipRestOfSExp(t);\n      return;\n    }\n    String id = parseIdentifier(t);\n\n    // Ignore args\n    t.consume(\"(\");\n    skipRestOfSExp(t);\n\n    // Parse type\n    parseType(t);\n\n    SolverVariable x = variables.get(id);\n    if (x != null) {\n      FunctorType ft = (FunctorType) x.getSymbol().getCompileTimeType();\n      AlgebraicDataType type = (AlgebraicDataType) ft.getRetType();\n      type = stripSymType(type);\n\n      if (shouldRecord(type)) {\n        m.put(x, parseTerm(t, type));\n      }\n    }\n    skipRestOfSExp(t);\n  }\n\n  private static enum TermType {\n    BV32,\n    BV64,\n    FP32,\n    FP64,\n    STRING,\n    ADT\n  }\n\n  public static AlgebraicDataType stripSymType(AlgebraicDataType symType) {\n    assert symType.getSymbol().equals(BuiltInTypeSymbol.SYM_TYPE);\n    return (AlgebraicDataType) symType.getTypeArgs().get(0);\n  }\n\n  public static boolean shouldRecord(AlgebraicDataType type) throws SmtLibParseException {\n    Set<Symbol> seen = new HashSet<>();\n    boolean ok = shouldRecord1(type, seen);\n    for (Type arg : type.getTypeArgs()) {\n      if (arg instanceof AlgebraicDataType) {\n        ok &= shouldRecord1((AlgebraicDataType) arg, seen);\n      }\n    }\n    return ok;\n  }\n\n  private static void die(String msg) throws SmtLibParseException {\n    throw new SmtLibParseException(\"INTERNAL ERROR: \" + msg);\n  }\n\n  private static boolean shouldRecord1(AlgebraicDataType type, Set<Symbol> seen)\n      throws SmtLibParseException {\n    TypeSymbol sym = type.getSymbol();\n    if (!seen.add(sym)) {\n      return true;\n    }\n    if (sym instanceof BuiltInTypeSymbol) {\n      switch ((BuiltInTypeSymbol) sym) {\n        case BOOL_TYPE:\n        case CMP_TYPE:\n        case LIST_TYPE:\n        case OPTION_TYPE:\n        case STRING_TYPE:\n          return true;\n        case ARRAY_TYPE:\n        case INT_TYPE:\n          return false;\n        case BV:\n          TypeIndex idx = (TypeIndex) type.getTypeArgs().get(0);\n          int w = idx.getIndex();\n          return w == 32 || w == 64;\n        case FP:\n          TypeIndex idx1 = (TypeIndex) type.getTypeArgs().get(0);\n          int e = idx1.getIndex();\n          TypeIndex idx2 = (TypeIndex) type.getTypeArgs().get(1);\n          int s = idx2.getIndex();\n          return (e == 8 && s == 24) || (e == 11 && s == 53);\n        case SMT_TYPE:\n        case MODEL_TYPE:\n        case SYM_TYPE:\n        default:\n          die(\"unexpected built-in symbol: \" + sym);\n      }\n    }\n    if (sym.isUninterpretedSort()) {\n      return false;\n    }\n    boolean ok = true;\n    if (type.hasConstructors()) {\n      for (ConstructorScheme cs : type.getConstructors()) {\n        for (Type ty : cs.getTypeArgs()) {\n          if (ty instanceof AlgebraicDataType) {\n            ok &= shouldRecord1((AlgebraicDataType) ty, seen);\n          }\n        }\n      }\n    }\n    return ok;\n  }\n\n  private TermType getTermType(AlgebraicDataType type) throws SmtLibParseException {\n    TypeSymbol sym = type.getSymbol();\n    if (sym instanceof BuiltInTypeSymbol) {\n      switch ((BuiltInTypeSymbol) sym) {\n        case BOOL_TYPE:\n        case CMP_TYPE:\n        case LIST_TYPE:\n        case OPTION_TYPE:\n          return TermType.ADT;\n        case STRING_TYPE:\n          return TermType.STRING;\n        case BV:\n          {\n            TypeIndex idx = (TypeIndex) type.getTypeArgs().get(0);\n            int w = idx.getIndex();\n            if (w == 32) {\n              return TermType.BV32;\n            } else if (w == 64) {\n              return TermType.BV64;\n            }\n            die(\"unexpected BV width \" + w);\n          }\n        case FP:\n          {\n            TypeIndex idx1 = (TypeIndex) type.getTypeArgs().get(0);\n            int e = idx1.getIndex();\n            TypeIndex idx2 = (TypeIndex) type.getTypeArgs().get(1);\n            int s = idx2.getIndex();\n            if (e == 8 && s == 24) {\n              return TermType.FP32;\n            } else if (e == 11 && s == 53) {\n              return TermType.FP64;\n            }\n            die(\"unexpected FP dimensions \" + e + \"/\" + s);\n          }\n        default:\n          die(\"unexpected built-in symbol: \" + sym);\n      }\n    }\n    return TermType.ADT;\n  }\n\n  private String parseIdentifier(Tokenizer t) throws IOException, SmtLibParseException {\n    String s = t.next();\n    if (s.equals(\"|\")) {\n      s = \"\";\n      while (!t.peek().equals(\"|\")) {\n        s += t.next();\n      }\n    } else {\n      if (t.peek().equals(\"!\")) {\n        t.consume(\"!\");\n        s += \"!\";\n        s += parseIdentifier(t);\n      }\n    }\n    return s;\n  }\n\n  private void parseType(Tokenizer t) throws IOException, SmtLibParseException {\n    String s = t.next();\n    if (s.equals(\"(\")) {\n      skipRestOfSExp(t);\n    }\n  }\n\n  private long parseBv(Tokenizer t) throws IOException, SmtLibParseException {\n    t.consume(\"#\");\n    String tok = t.next();\n    String prefix = tok.substring(0, 1);\n    String num = tok.substring(1);\n    int base = 0;\n    if (prefix.equals(\"b\")) {\n      base = 2;\n    } else {\n      assert (prefix.equals(\"x\"));\n      base = 16;\n    }\n    return Long.parseUnsignedLong(num, base);\n  }\n\n  private Term parseTerm(Tokenizer t, AlgebraicDataType type)\n      throws IOException, SmtLibParseException {\n    switch (getTermType(type)) {\n      case ADT:\n        return parseADTTerm(t, type);\n      case BV32:\n        {\n          return I32.make((int) parseBv(t));\n        }\n      case BV64:\n        {\n          return I64.make(parseBv(t));\n        }\n      // FIXME I'm not sure if these conversions to floating point are 100%\n      // correct...\n      case FP32:\n        {\n          float val = -1;\n          t.consume(\"(\");\n          if (t.peek().equals(\"fp\")) {\n            t.consume(\"fp\");\n            long sign = parseBv(t);\n            long exp = parseBv(t);\n            long mant = parseBv(t);\n            long bits = sign << 31;\n            bits |= exp << 23;\n            bits |= mant;\n            val = Float.intBitsToFloat((int) bits);\n          } else {\n            t.consume(\"_\");\n            String next = t.next();\n            if (next.equals(\"NaN\")) {\n              val = Float.NaN;\n            } else if (next.equals(\"+\")) {\n              if (t.peek().equals(\"oo\")) {\n                t.consume(\"oo\");\n                val = Float.POSITIVE_INFINITY;\n              } else {\n                t.consume(\"zero\");\n                val = +0.0f;\n              }\n            } else {\n              assert next.equals(\"-\");\n              if (t.peek().equals(\"oo\")) {\n                t.consume(\"oo\");\n                val = Float.NEGATIVE_INFINITY;\n              } else {\n                t.consume(\"zero\");\n                val = -0.0f;\n              }\n            }\n          }\n          skipRestOfSExp(t);\n          return FP32.make(val);\n        }\n      case FP64:\n        {\n          double val = -1;\n          t.consume(\"(\");\n          if (t.peek().equals(\"fp\")) {\n            t.consume(\"fp\");\n            long sign = parseBv(t);\n            long exp = parseBv(t);\n            long mant = parseBv(t);\n            long bits = sign << 63;\n            bits |= exp << 52;\n            bits |= mant;\n            val = Double.longBitsToDouble(bits);\n          } else {\n            t.consume(\"_\");\n            String next = t.next();\n            if (next.equals(\"NaN\")) {\n              val = Double.NaN;\n            } else if (next.equals(\"+\")) {\n              if (t.peek().equals(\"oo\")) {\n                t.consume(\"oo\");\n                val = Double.POSITIVE_INFINITY;\n              } else {\n                t.consume(\"zero\");\n                val = +0.0;\n              }\n            } else {\n              assert next.equals(\"-\");\n              if (t.peek().equals(\"oo\")) {\n                t.consume(\"oo\");\n                val = Double.NEGATIVE_INFINITY;\n              } else {\n                t.consume(\"zero\");\n                val = -0.0;\n              }\n            }\n          }\n          skipRestOfSExp(t);\n          return FP64.make(val);\n        }\n      case STRING:\n        return parseString(t);\n    }\n    die(\"unexpected term type: \" + getTermType(type));\n    return null;\n  }\n\n  private Term parseString(Tokenizer t) throws IOException, SmtLibParseException {\n    t.consume(\"\\\"\");\n    t.ignoreWhitespace(false);\n    StringBuilder sb = new StringBuilder();\n    while (true) {\n      String next = t.next();\n      if (next.equals(\"\\\"\")) {\n        // SMT-LIB uses \"\" to represent the character \"\n        if (!t.peek().equals(\"\\\"\")) {\n          break;\n        }\n        t.consume(\"\\\"\");\n      }\n      sb.append(next);\n    }\n    t.ignoreWhitespace(true);\n    return StringTerm.make(sb.toString());\n  }\n\n  private Term parseADTTerm(Tokenizer t, AlgebraicDataType type)\n      throws IOException, SmtLibParseException {\n    String id = t.next();\n    if (id.equals(\"(\")) {\n      id = t.next();\n      if (id.equals(\"as\")) {\n        Term term = parseADTTerm(t, type);\n        skipRestOfSExp(t);\n        return term;\n      }\n    }\n    if (id.equals(\"true\")) {\n      return BoolTerm.mkTrue();\n    }\n    if (id.equals(\"false\")) {\n      return BoolTerm.mkFalse();\n    }\n    ConstructorSymbol sym = (ConstructorSymbol) symbolManager.lookupSymbol(id);\n    Term[] args = Terms.emptyArray();\n    if (sym.getArity() > 0) {\n      List<Type> argTypes = null;\n      for (ConstructorScheme cs : type.getConstructors()) {\n        if (cs.getSymbol().equals(sym)) {\n          argTypes = cs.getTypeArgs();\n          break;\n        }\n      }\n      assert argTypes != null;\n      args = new Term[argTypes.size()];\n      int i = 0;\n      for (Type ty : argTypes) {\n        Term arg = parseTerm(t, (AlgebraicDataType) ty);\n        args[i] = arg;\n        ++i;\n      }\n      skipRestOfSExp(t);\n    }\n    return Constructors.make(sym, args);\n  }\n\n  private void skipRestOfSExp(Tokenizer t) throws IOException, SmtLibParseException {\n    int depth = 0;\n    while (depth >= 0) {\n      switch (t.next()) {\n        case \"(\":\n          depth++;\n          break;\n        case \")\":\n          depth--;\n          break;\n      }\n    }\n  }\n\n  private static class Tokenizer {\n\n    private final StreamTokenizer t;\n\n    public Tokenizer(Reader r) {\n      t = new StreamTokenizer(r);\n      t.ordinaryChar('.');\n      t.ordinaryChar('-');\n      t.ordinaryChars('0', '9');\n      t.ordinaryChar('\"');\n      t.ordinaryChar('\\'');\n      t.ordinaryChar('/');\n      t.wordChars('0', '9');\n      t.wordChars('_', '_');\n    }\n\n    // FIXME This is almost certainly incomplete.\n    public void ignoreWhitespace(boolean ignore) {\n      if (ignore) {\n        t.whitespaceChars('\\n', '\\n');\n        t.whitespaceChars('\\r', '\\r');\n        t.whitespaceChars('\\t', '\\t');\n        t.whitespaceChars(' ', ' ');\n      } else {\n        t.ordinaryChar('\\n');\n        t.ordinaryChar('\\r');\n        t.ordinaryChar('\\t');\n        t.ordinaryChar(' ');\n      }\n    }\n\n    public String peek() throws IOException, SmtLibParseException {\n      String token = next();\n      t.pushBack();\n      return token;\n    }\n\n    public String next() throws IOException, SmtLibParseException {\n      int token = t.nextToken();\n      switch (t.ttype) {\n        case StreamTokenizer.TT_EOF:\n          throw new SmtLibParseException(\"Unexpected EOF.\");\n        case StreamTokenizer.TT_EOL:\n          // This should only happen in the mode when whitespace is significant.\n          return \"\\n\";\n        case StreamTokenizer.TT_NUMBER:\n          die(\"nothing should be tokenized as a number.\");\n        case StreamTokenizer.TT_WORD:\n          return t.sval;\n        default:\n          return Character.toString((char) token);\n      }\n    }\n\n    public boolean hasNext() throws IOException {\n      t.nextToken();\n      t.pushBack();\n      return t.ttype != StreamTokenizer.TT_EOF;\n    }\n\n    public void consume(String s) throws IOException, SmtLibParseException {\n      Tokenizer t = new Tokenizer(new StringReader(s));\n      while (t.hasNext()) {\n        String expected = t.next();\n        String found = next();\n        if (!expected.equals(found)) {\n          throw new SmtLibParseException(\n              \"Tried to consume \\\"\" + expected + \"\\\", but found \\\"\" + found + \"\\\".\");\n        }\n      }\n    }\n  }\n\n  @SuppressWarnings(\"serial\")\n  public static class SmtLibParseException extends Exception {\n\n    public SmtLibParseException(String message) {\n      super(message);\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/smt/SmtLibShim.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.smt;\n\nimport edu.harvard.seas.pl.formulog.Configuration;\nimport edu.harvard.seas.pl.formulog.Main;\nimport edu.harvard.seas.pl.formulog.ast.Constructor;\nimport edu.harvard.seas.pl.formulog.ast.Constructors.SolverVariable;\nimport edu.harvard.seas.pl.formulog.ast.Expr;\nimport edu.harvard.seas.pl.formulog.ast.Primitive;\nimport edu.harvard.seas.pl.formulog.ast.Program;\nimport edu.harvard.seas.pl.formulog.ast.SmtLibTerm;\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.ast.Terms.TermVisitor;\nimport edu.harvard.seas.pl.formulog.ast.Terms.TermVisitorExn;\nimport edu.harvard.seas.pl.formulog.ast.Var;\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport edu.harvard.seas.pl.formulog.smt.SmtLibParser.SmtLibParseException;\nimport edu.harvard.seas.pl.formulog.symbols.BuiltInTypeSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.ConstructorSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.ConstructorSymbolType;\nimport edu.harvard.seas.pl.formulog.symbols.Symbol;\nimport edu.harvard.seas.pl.formulog.symbols.SymbolManager;\nimport edu.harvard.seas.pl.formulog.symbols.TypeSymbol;\nimport edu.harvard.seas.pl.formulog.types.FunctorType;\nimport edu.harvard.seas.pl.formulog.types.TypeChecker;\nimport edu.harvard.seas.pl.formulog.types.Types;\nimport edu.harvard.seas.pl.formulog.types.Types.AlgebraicDataType;\nimport edu.harvard.seas.pl.formulog.types.Types.AlgebraicDataType.ConstructorScheme;\nimport edu.harvard.seas.pl.formulog.types.Types.OpaqueType;\nimport edu.harvard.seas.pl.formulog.types.Types.Type;\nimport edu.harvard.seas.pl.formulog.types.Types.TypeIndex;\nimport edu.harvard.seas.pl.formulog.types.Types.TypeVar;\nimport edu.harvard.seas.pl.formulog.types.Types.TypeVisitor;\nimport edu.harvard.seas.pl.formulog.util.DedupWorkList;\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport java.io.BufferedReader;\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.io.Reader;\nimport java.io.Writer;\nimport java.util.ArrayDeque;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Deque;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.NoSuchElementException;\nimport java.util.Set;\nimport org.apache.commons.lang3.time.StopWatch;\nimport org.jgrapht.Graph;\nimport org.jgrapht.alg.connectivity.KosarajuStrongConnectivityInspector;\nimport org.jgrapht.alg.interfaces.StrongConnectivityAlgorithm;\nimport org.jgrapht.graph.DefaultDirectedGraph;\nimport org.jgrapht.graph.DefaultEdge;\nimport org.jgrapht.traverse.TopologicalOrderIterator;\n\npublic class SmtLibShim {\n\n  private static final boolean recordTime = Configuration.timeSmt;\n\n  private final BufferedReader in;\n  private PrintWriter out;\n  private final Map<SolverVariable, String> declaredSymbols = new HashMap<>();\n  private final Deque<Set<SolverVariable>> symbolsByStackPos = new ArrayDeque<>();\n  private final Map<String, SolverVariable> symbolLookup = new HashMap<>();\n  private PrintWriter log;\n  private Iterator<Pair<ConstructorSymbol, Type>> typeAnnotations;\n\n  private SymbolManager symbolManager;\n  private final List<String> declarations = new ArrayList<>();\n\n  public SmtLibShim(Reader in, Writer out) {\n    this(in, out, null);\n  }\n\n  public SmtLibShim(Reader in, Writer out, Writer log) {\n    this.in = in != null ? new BufferedReader(in) : null;\n    this.out = new PrintWriter(out);\n    this.log = log != null ? new PrintWriter(log) : null;\n    symbolsByStackPos.add(new HashSet<>());\n  }\n\n  public void initialize(Program<?, ?> prog, boolean declareAdts) {\n    symbolManager = prog.getSymbolManager();\n    new DeclarationGatherer(declareAdts).go(prog);\n    if (Configuration.smtCheckSuccess) {\n      println(\"(set-option :print-success true)\");\n    }\n    try {\n      checkSuccess();\n    } catch (EvaluationException e) {\n      throw new AssertionError(e);\n    }\n  }\n\n  private void checkSuccess() throws EvaluationException {\n    if (in != null && Configuration.smtCheckSuccess) {\n      flush();\n      try {\n        String r = in.readLine();\n        if (log != null) {\n          log.println(\"; success? \" + r);\n          log.flush();\n        }\n        if (r == null || !r.equals(\"success\")) {\n          throw new EvaluationException(\"Solver did not return success: \" + r);\n        }\n      } catch (IOException e) {\n        throw new EvaluationException(\"Problem with evaluating solver: \" + e.getMessage());\n      }\n    }\n  }\n\n  public void makeAssertion(SmtLibTerm assertion) throws EvaluationException {\n    long start = 0;\n    long end = 0;\n    if (recordTime) {\n      start = System.nanoTime();\n    }\n    declareSymbols(assertion);\n    if (recordTime) {\n      end = System.nanoTime();\n      Configuration.recordSmtDeclTime(end - start);\n      start = end;\n    }\n    typeAnnotations = new MiniTypeInferer().inferTypes(assertion).iterator();\n    if (recordTime) {\n      end = System.nanoTime();\n      Configuration.recordSmtInferTime(end - start);\n      start = end;\n    }\n    print(\"(assert \");\n    assertion.toSmtLib(this);\n    println(\")\");\n    checkSuccess();\n    if (recordTime) {\n      end = System.nanoTime();\n      Configuration.recordSmtSerialTime(end - start);\n    }\n    assert !typeAnnotations.hasNext() : typeAnnotations.next();\n  }\n\n  public void reset() throws EvaluationException {\n    declaredSymbols.clear();\n    symbolLookup.clear();\n    symbolsByStackPos.clear();\n    symbolsByStackPos.add(new HashSet<>());\n    println(\"(reset)\");\n    checkSuccess();\n  }\n\n  public void resetAssertions() throws EvaluationException {\n    println(\"(reset-assertions)\");\n    checkSuccess();\n  }\n\n  public void push() throws EvaluationException {\n    // Avoid push timing out. See <https://github.com/Z3Prover/z3/issues/4713>\n    setTimeout(Integer.MAX_VALUE);\n    println(\"(push 1)\");\n    checkSuccess();\n    symbolsByStackPos.addLast(new HashSet<>());\n  }\n\n  public void pop() throws EvaluationException {\n    pop(1);\n  }\n\n  public void pop(int n) throws EvaluationException {\n    println(\"(pop \" + n + \")\");\n    checkSuccess();\n    for (int i = 0; i < n; ++i) {\n      for (SolverVariable x : symbolsByStackPos.removeLast()) {\n        String s = declaredSymbols.remove(x);\n        symbolLookup.remove(s);\n      }\n    }\n  }\n\n  public SmtStatus checkSat(int timeout) throws EvaluationException {\n    return checkSatAssuming(Collections.emptyList(), Collections.emptyList(), timeout);\n  }\n\n  private void setTimeout(int timeout) throws EvaluationException {\n    if (timeout < 0) {\n      System.err.println(\"Warning: negative timeout provided to solver - ignored\");\n      timeout = Integer.MAX_VALUE;\n    }\n    if (Configuration.smtSolver.equals(\"z3\")) {\n      println(\"(set-option :timeout \" + timeout + \")\");\n      checkSuccess();\n    }\n  }\n\n  public SmtStatus checkSatAssuming(\n      Collection<SolverVariable> onVars, Collection<SolverVariable> offVars, int timeout)\n      throws EvaluationException {\n    setTimeout(timeout);\n    if (onVars.isEmpty() && offVars.isEmpty()) {\n      println(\"(check-sat)\");\n    } else {\n      print(\"(check-sat-assuming (\");\n      for (SolverVariable x : onVars) {\n        print(x);\n        print(\" \");\n      }\n      for (SolverVariable x : offVars) {\n        print(\"(not \");\n        print(x);\n        print(\") \");\n      }\n      println(\"))\");\n    }\n    flush();\n    String result;\n    try {\n      StopWatch clock = null;\n      if (Main.smtStats) {\n        clock = new StopWatch();\n        clock.start();\n      }\n      result = in.readLine();\n      if (Main.smtStats) {\n        Configuration.smtTime.get().add(clock.getTime());\n      }\n      if (result == null) {\n        throw new EvaluationException(\"Problem with evaluating solver! Unexpected end of stream\");\n      }\n      if (log != null) {\n        log.println(\"; result: \" + result);\n        log.flush();\n      }\n      if (result.equals(\"sat\")) {\n        return SmtStatus.SATISFIABLE;\n      } else if (result.equals(\"unsat\")) {\n        return SmtStatus.UNSATISFIABLE;\n      } else if (result.equals(\"unknown\")) {\n        return SmtStatus.UNKNOWN;\n      } else {\n        throw new EvaluationException(\n            \"Problem with evaluating solver! Unexpected result: \" + result);\n      }\n    } catch (IOException e) {\n      throw new EvaluationException(\"Problem with evaluating solver: \" + e.getMessage());\n    }\n  }\n\n  public Map<SolverVariable, Term> getModel() throws EvaluationException {\n    println(\"(get-model)\");\n    flush();\n    try {\n      return parseModel();\n    } catch (IOException e) {\n      throw new EvaluationException(\"Problem with evaluating solver: \" + e.getMessage());\n    } catch (SmtLibParseException e) {\n      throw new EvaluationException(\"Problem parsing solver output: \" + e.getMessage());\n    }\n  }\n\n  public Map<SolverVariable, Term> parseModel()\n      throws EvaluationException, IOException, SmtLibParseException {\n    SmtLibParser p = new SmtLibParser(symbolManager, symbolLookup);\n    return p.getModel(in);\n  }\n\n  public void flush() {\n    out.flush();\n    if (log != null) {\n      log.flush();\n    }\n  }\n\n  public void setLogic(String logic) throws EvaluationException {\n    println(\"(set-logic \" + logic + \")\");\n    checkSuccess();\n  }\n\n  public void print(String s) {\n    out.print(s);\n    if (log != null) {\n      log.print(s);\n    }\n  }\n\n  private void println(String s) {\n    print(s);\n    print(\"\\n\");\n  }\n\n  public void printComment(String comment) {\n    println(\"; \" + comment);\n  }\n\n  public void print(SolverVariable x) {\n    String s = declaredSymbols.get(x);\n    if (s == null) {\n      throw new NoSuchElementException(x.toString());\n    }\n    print(s);\n  }\n\n  public void print(Symbol sym) {\n    print(stringifySymbol(sym));\n  }\n\n  public void print(Type type) {\n    print(stringifyType(type));\n  }\n\n  public String getTypeAnnotation(ConstructorSymbol sym) {\n    if (needsTypeAnnotation(sym)) {\n      Pair<ConstructorSymbol, Type> p = typeAnnotations.next();\n      assert p.fst().equals(sym);\n      return stringifyType(p.snd());\n    }\n    return null;\n  }\n\n  private String toSmtSymbol(SolverVariable x) {\n    return \"x\" + x.getSolverVarId();\n  }\n\n  private void declareSymbols(SmtLibTerm t) throws EvaluationException {\n    t.accept(\n        new TermVisitorExn<Void, Void, EvaluationException>() {\n\n          @Override\n          public Void visit(Var t, Void in) {\n            throw new AssertionError(\"impossible\");\n          }\n\n          @Override\n          public Void visit(Constructor c, Void in) throws EvaluationException {\n            if (c instanceof SolverVariable) {\n              SolverVariable var = (SolverVariable) c;\n              if (!declaredSymbols.containsKey(var)) {\n                String s = toSmtSymbol(var);\n                declaredSymbols.put(var, s);\n                symbolLookup.put(s, var);\n                symbolsByStackPos.getLast().add(var);\n                print(\"(declare-fun \" + s + \" () \");\n                FunctorType ft = (FunctorType) var.getSymbol().getCompileTimeType();\n                print(stringifyType(ft.getRetType()));\n                println(\")\");\n                checkSuccess();\n              }\n              return null;\n            }\n            for (Term arg : c.getArgs()) {\n              arg.accept(this, in);\n            }\n            return null;\n          }\n\n          @Override\n          public Void visit(Primitive<?> p, Void in) {\n            return null;\n          }\n\n          @Override\n          public Void visit(Expr expr, Void in) {\n            throw new AssertionError(\"impossible\");\n          }\n        },\n        null);\n  }\n\n  public void makeDeclarations() {\n    long start = 0;\n    if (Configuration.timeSmt) {\n      start = System.nanoTime();\n    }\n    for (String decl : declarations) {\n      println(decl);\n      try {\n        checkSuccess();\n      } catch (EvaluationException e) {\n        System.err.println(\n            \"WARNING: solver rejected declaration:\\n\" + decl + \"\\n\" + e.getMessage());\n      }\n    }\n    if (Configuration.timeSmt) {\n      Configuration.recordSmtDeclGlobalsTime(System.nanoTime() - start);\n    }\n  }\n\n  private class DeclarationGatherer {\n\n    private final ByteArrayOutputStream baos = new ByteArrayOutputStream();\n    private final boolean declareAdts;\n\n    public DeclarationGatherer(boolean declareAdts) {\n      this.declareAdts = declareAdts;\n    }\n\n    public void go(Program<?, ?> prog) {\n      PrintWriter tmpLog = log;\n      log = null;\n      PrintWriter tmpOut = out;\n      out = new PrintWriter(baos);\n      declareSorts(prog.getTypeSymbols());\n      declareUninterpretedFunctions(prog.getUninterpretedFunctionSymbols());\n      out = tmpOut;\n      log = tmpLog;\n    }\n\n    private void pushDeclaration() {\n      flush();\n      declarations.add(baos.toString());\n      baos.reset();\n    }\n\n    private void declareUninterpretedFunctions(Set<ConstructorSymbol> funcs) {\n      for (ConstructorSymbol func : funcs) {\n        print(\"(declare-fun \" + stringifySymbol(func) + \" (\");\n        FunctorType ft = func.getCompileTimeType();\n        for (Iterator<Type> it = ft.getArgTypes().iterator(); it.hasNext(); ) {\n          print(stringifyType(it.next()));\n          if (it.hasNext()) {\n            print(\" \");\n          }\n        }\n        print(\") \" + stringifyType(ft.getRetType()) + \")\");\n        pushDeclaration();\n      }\n    }\n\n    private void declareSorts(Set<TypeSymbol> sorts) {\n      SortDependencyFinder depends = new SortDependencyFinder(sorts);\n      StrongConnectivityAlgorithm<TypeSymbol, DefaultEdge> k =\n          new KosarajuStrongConnectivityInspector<>(depends.compute());\n      TopologicalOrderIterator<Graph<TypeSymbol, DefaultEdge>, DefaultEdge> topo =\n          new TopologicalOrderIterator<>(k.getCondensation());\n      while (topo.hasNext()) {\n        Graph<TypeSymbol, DefaultEdge> scc = topo.next();\n        declareScc(scc.vertexSet());\n      }\n    }\n\n    private void declareScc(Set<TypeSymbol> sorts) {\n      assert !sorts.isEmpty();\n      TypeSymbol sym = sorts.iterator().next();\n      if (sym.isUninterpretedSort()) {\n        assert sorts.size() == 1;\n        declareUninterpretedSort(sym);\n      } else if (declareAdts) {\n        assert sym.isNormalType();\n        declareAdtSorts(sorts);\n      }\n    }\n\n    private void declareUninterpretedSort(TypeSymbol sort) {\n      assert sort.isUninterpretedSort();\n      print(\"(declare-sort \" + stringifySymbol(sort) + \" \" + sort.getArity() + \")\");\n      pushDeclaration();\n    }\n\n    private void declareAdtSorts(Set<TypeSymbol> sorts) {\n      assert !sorts.isEmpty();\n      print(\"(declare-datatypes ( \");\n      for (TypeSymbol sym : sorts) {\n        assert sym.isNormalType();\n        print(\"(\" + stringifySymbol(sym) + \" \" + sym.getArity() + \") \");\n      }\n      print(\") (\");\n      for (TypeSymbol sym : sorts) {\n        declareAdtSort(AlgebraicDataType.makeWithFreshArgs(sym));\n      }\n      print(\"))\");\n      pushDeclaration();\n    }\n\n    private void declareAdtSort(AlgebraicDataType type) {\n      print(\"\\n  (par (\");\n      for (Iterator<Type> it = type.getTypeArgs().iterator(); it.hasNext(); ) {\n        print(stringifyType(it.next()));\n        if (it.hasNext()) {\n          print(\" \");\n        }\n      }\n      print(\") (\");\n      for (ConstructorScheme c : type.getConstructors()) {\n        declareConstructor(c);\n      }\n      print(\"))\");\n    }\n\n    private void declareConstructor(ConstructorScheme c) {\n      print(\"\\n    (\");\n      print(stringifySymbol(c.getSymbol()));\n      Iterator<ConstructorSymbol> getterSyms = c.getGetterSymbols().iterator();\n      for (Type t : c.getTypeArgs()) {\n        String getter = stringifySymbol(getterSyms.next());\n        print(\" (\" + getter + \" \" + stringifyType(t) + \")\");\n      }\n      print(\")\");\n    }\n  }\n\n  private String stringifySymbol(Symbol sym) {\n    return \"|\" + sym + \"|\";\n  }\n\n  private String stringifyType(Type type) {\n    return type.accept(\n        new TypeVisitor<Void, String>() {\n\n          @Override\n          public String visit(TypeVar typeVar, Void in) {\n            return \"|\" + typeVar + \"|\";\n          }\n\n          @Override\n          public String visit(AlgebraicDataType algebraicType, Void in) {\n            TypeSymbol sym = algebraicType.getSymbol();\n            if (sym instanceof BuiltInTypeSymbol) {\n              List<Type> typeArgs = algebraicType.getTypeArgs();\n              switch ((BuiltInTypeSymbol) sym) {\n                case BOOL_TYPE:\n                  return \"Bool\";\n                case STRING_TYPE:\n                  return \"String\";\n                case ARRAY_TYPE:\n                  {\n                    String s = \"(Array \";\n                    for (Type t : typeArgs) {\n                      s += \" \" + stringifyType(t);\n                    }\n                    return s + \")\";\n                  }\n                case INT_TYPE:\n                  return \"Int\";\n                case SMT_TYPE:\n                case SYM_TYPE:\n                  return stringifyType(algebraicType.getTypeArgs().get(0));\n                case BV:\n                  return \"(_ BitVec \" + ((TypeIndex) typeArgs.get(0)).getIndex() + \")\";\n                case FP:\n                  int idx1 = ((TypeIndex) typeArgs.get(0)).getIndex();\n                  int idx2 = ((TypeIndex) typeArgs.get(1)).getIndex();\n                  return \"(_ FloatingPoint \" + idx1 + \" \" + idx2 + \")\";\n                case CMP_TYPE:\n                case LIST_TYPE:\n                case OPTION_TYPE:\n                  break;\n                case MODEL_TYPE:\n                case SMT_PATTERN_TYPE:\n                case SMT_WRAPPED_VAR_TYPE:\n                default:\n                  return null;\n              }\n            }\n            if (sym.getArity() == 0) {\n              return stringifySymbol(sym);\n            }\n            String s = \"(\" + stringifySymbol(sym);\n            for (Type t : algebraicType.getTypeArgs()) {\n              s += \" \" + stringifyType(t);\n            }\n            return s + \")\";\n          }\n\n          @Override\n          public String visit(OpaqueType opaqueType, Void in) {\n            throw new AssertionError(\"impossible\");\n          }\n\n          @Override\n          public String visit(TypeIndex typeIndex, Void in) {\n            throw new AssertionError(\"shouldn't happen\");\n          }\n        },\n        null);\n  }\n\n  private class SortDependencyFinder {\n\n    private final DedupWorkList<TypeSymbol> w = new DedupWorkList<>();\n\n    public SortDependencyFinder(Set<TypeSymbol> types) {\n      for (TypeSymbol type : types) {\n        push(type);\n      }\n    }\n\n    private void push(TypeSymbol sym) {\n      if (isDeclarableTypeSymbol(sym)) {\n        w.push(sym);\n      }\n    }\n\n    private Graph<TypeSymbol, DefaultEdge> compute() {\n      Graph<TypeSymbol, DefaultEdge> g = new DefaultDirectedGraph<>(DefaultEdge.class);\n      while (!w.isEmpty()) {\n        TypeSymbol sym = w.pop();\n        assert isDeclarableTypeSymbol(sym);\n        g.addVertex(sym);\n        AlgebraicDataType type = AlgebraicDataType.makeWithFreshArgs(sym);\n        if (sym.isUninterpretedSort()) {\n          continue;\n        }\n        for (ConstructorScheme c : type.getConstructors()) {\n          for (Type typeArg : c.getTypeArgs()) {\n            for (TypeSymbol other : extractTypeSymbols(typeArg)) {\n              push(other);\n              if (isDeclarableTypeSymbol(other)) {\n                g.addVertex(other);\n                g.addEdge(other, sym);\n              }\n            }\n          }\n        }\n      }\n      return g;\n    }\n\n    private Set<TypeSymbol> extractTypeSymbols(Type type) {\n      Set<TypeSymbol> syms = new HashSet<>();\n      type.accept(\n          new TypeVisitor<Void, Void>() {\n\n            @Override\n            public Void visit(TypeVar typeVar, Void in) {\n              return null;\n            }\n\n            @Override\n            public Void visit(AlgebraicDataType algebraicType, Void in) {\n              syms.add(algebraicType.getSymbol());\n              for (Type typeArg : algebraicType.getTypeArgs()) {\n                typeArg.accept(this, in);\n              }\n              return null;\n            }\n\n            @Override\n            public Void visit(OpaqueType opaqueType, Void in) {\n              throw new AssertionError(\"impossible\");\n            }\n\n            @Override\n            public Void visit(TypeIndex typeIndex, Void in) {\n              return null;\n            }\n          },\n          null);\n      return syms;\n    }\n\n    private boolean isDeclarableTypeSymbol(TypeSymbol sym) {\n      if (sym.isAlias()) {\n        return false;\n      }\n      if (sym instanceof BuiltInTypeSymbol) {\n        switch ((BuiltInTypeSymbol) sym) {\n          case SMT_TYPE:\n          case SYM_TYPE:\n          case BOOL_TYPE:\n          case STRING_TYPE:\n          case ARRAY_TYPE:\n          case INT_TYPE:\n          case MODEL_TYPE:\n          case BV:\n          case FP:\n          case SMT_PATTERN_TYPE:\n          case SMT_WRAPPED_VAR_TYPE:\n          case OPAQUE_SET:\n            return false;\n          case CMP_TYPE:\n          case LIST_TYPE:\n          case OPTION_TYPE:\n            return true;\n          default:\n            throw new AssertionError(\"impossible\");\n        }\n      }\n      return true;\n    }\n  }\n\n  private class MiniTypeInferer {\n\n    private final Deque<Pair<Type, Type>> constraints = new ArrayDeque<>();\n    private final Map<TypeVar, Type> subst = new HashMap<>();\n\n    public List<Pair<ConstructorSymbol, Type>> inferTypes(Term t) {\n      constraints.clear();\n      subst.clear();\n      List<Pair<ConstructorSymbol, Type>> types = inferTypes1(t);\n      unifyConstraints();\n      List<Pair<ConstructorSymbol, Type>> types2 = new ArrayList<>();\n      for (Pair<ConstructorSymbol, Type> p : types) {\n        types2.add(new Pair<>(p.fst(), TypeChecker.simplify(p.snd().applySubst(subst))));\n      }\n      return types2;\n    }\n\n    private List<Pair<ConstructorSymbol, Type>> inferTypes1(Term t) {\n      List<Pair<ConstructorSymbol, Type>> types = new ArrayList<>();\n      t.accept(\n          new TermVisitor<Void, Type>() {\n\n            @Override\n            public Type visit(Var t, Void in) {\n              throw new AssertionError(\"impossible\");\n            }\n\n            @Override\n            public Type visit(Constructor c, Void in) {\n              ConstructorSymbol sym = c.getSymbol();\n              FunctorType ft = sym.getCompileTimeType().freshen();\n              Type ty = ft.getRetType();\n              if (needsTypeAnnotation(sym)) {\n                types.add(new Pair<>(sym, ty));\n              }\n              if (!(c instanceof SolverVariable)) {\n                Iterator<Type> it = ft.getArgTypes().iterator();\n                for (Term tt : c.getArgs()) {\n                  constraints.add(new Pair<>(tt.accept(this, in), it.next()));\n                }\n              }\n              return ty;\n            }\n\n            @Override\n            public Type visit(Primitive<?> p, Void in) {\n              return p.getType().freshen();\n            }\n\n            @Override\n            public Type visit(Expr expr, Void in) {\n              throw new AssertionError(\"impossible\");\n            }\n          },\n          null);\n      return types;\n    }\n\n    private void unifyConstraints() {\n      TypeVisitor<Type, Void> unifier =\n          new TypeVisitor<Type, Void>() {\n\n            @Override\n            public Void visit(TypeVar typeVar, Type other) {\n              throw new AssertionError(\"impossible\");\n            }\n\n            @Override\n            public Void visit(AlgebraicDataType algebraicType, Type other) {\n              AlgebraicDataType otherAdt = (AlgebraicDataType) other;\n              Iterator<Type> it = otherAdt.getTypeArgs().iterator();\n              for (Type arg : algebraicType.getTypeArgs()) {\n                constraints.add(new Pair<>(arg, it.next()));\n              }\n              return null;\n            }\n\n            @Override\n            public Void visit(OpaqueType opaqueType, Type other) {\n              throw new AssertionError(\"impossible\");\n            }\n\n            @Override\n            public Void visit(TypeIndex typeIndex, Type other) {\n              assert typeIndex.equals(other);\n              return null;\n            }\n          };\n      while (!constraints.isEmpty()) {\n        Pair<Type, Type> p = constraints.pop();\n        Type t1 = TypeChecker.simplify(TypeChecker.lookupType(p.fst(), subst));\n        Type t2 = TypeChecker.simplify(TypeChecker.lookupType(p.snd(), subst));\n        if (t1.isVar()) {\n          handleVar((TypeVar) t1, t2);\n        } else if (t2.isVar()) {\n          handleVar((TypeVar) t2, t1);\n        } else {\n          t1.accept(unifier, t2);\n        }\n      }\n    }\n\n    private void handleVar(TypeVar x, Type t) {\n      if (t.isVar()) {\n        TypeVar y = (TypeVar) t;\n        if (x.compareTo(y) < 0) {\n          subst.put(x, y);\n        } else if (x.compareTo(y) > 0) {\n          subst.put(y, x);\n        }\n        return;\n      }\n      subst.put(x, t);\n    }\n  }\n\n  public static boolean needsTypeAnnotation(ConstructorSymbol sym) {\n    if (sym.getConstructorSymbolType().equals(ConstructorSymbolType.VANILLA_CONSTRUCTOR)) {\n      return true;\n    }\n    FunctorType ft = sym.getCompileTimeType();\n    List<Type> args = ft.getArgTypes();\n    Type ret = sym.getCompileTimeType().getRetType();\n    return !Types.getTypeVars(args).containsAll(Types.getTypeVars(ret));\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/smt/SmtLibSolver.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.smt;\n\nimport edu.harvard.seas.pl.formulog.ast.Program;\nimport edu.harvard.seas.pl.formulog.ast.SmtLibTerm;\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport java.util.Collection;\n\npublic interface SmtLibSolver {\n\n  void start(Program<?, ?> prog) throws EvaluationException;\n\n  SmtResult check(Collection<SmtLibTerm> t, boolean getModel, int timeout)\n      throws EvaluationException;\n\n  void destroy();\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/smt/SmtResult.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.smt;\n\nimport edu.harvard.seas.pl.formulog.ast.Model;\n\npublic class SmtResult {\n\n  public final SmtStatus status;\n  public final Model model;\n  public final int solverId;\n  public final int taskId;\n\n  public SmtResult(SmtStatus status, Model model, int solverId, int taskId) {\n    this.status = status;\n    this.model = model;\n    this.solverId = solverId;\n    this.taskId = taskId;\n  }\n\n  @Override\n  public int hashCode() {\n    final int prime = 31;\n    int result = 1;\n    result = prime * result + ((model == null) ? 0 : model.hashCode());\n    result = prime * result + solverId;\n    result = prime * result + ((status == null) ? 0 : status.hashCode());\n    result = prime * result + taskId;\n    return result;\n  }\n\n  @Override\n  public boolean equals(Object obj) {\n    if (this == obj) return true;\n    if (obj == null) return false;\n    if (getClass() != obj.getClass()) return false;\n    SmtResult other = (SmtResult) obj;\n    if (model == null) {\n      if (other.model != null) return false;\n    } else if (!model.equals(other.model)) return false;\n    if (solverId != other.solverId) return false;\n    if (status != other.status) return false;\n    if (taskId != other.taskId) return false;\n    return true;\n  }\n\n  @Override\n  public String toString() {\n    return \"SmtResult [status=\"\n        + status\n        + \", model=\"\n        + model\n        + \", solverId=\"\n        + solverId\n        + \", taskId=\"\n        + taskId\n        + \"]\";\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/smt/SmtStatus.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.smt;\n\npublic enum SmtStatus {\n  SATISFIABLE,\n  UNSATISFIABLE,\n  UNKNOWN\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/smt/SmtStrategy.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.smt;\n\npublic class SmtStrategy {\n\n  public enum Tag {\n    QUEUE,\n\n    BEST_MATCH,\n\n    NAIVE,\n\n    PUSH_POP,\n\n    PUSH_POP_NAIVE,\n\n    PER_THREAD_QUEUE,\n\n    PER_THREAD_BEST_MATCH,\n\n    PER_THREAD_PUSH_POP,\n\n    PER_THREAD_NAIVE,\n\n    PER_THREAD_PUSH_POP_NAIVE,\n    ;\n  }\n\n  private final Tag tag;\n  private final Object metadata;\n\n  public SmtStrategy(Tag tag, Object metadata) {\n    this.tag = tag;\n    this.metadata = metadata;\n  }\n\n  public Tag getTag() {\n    return tag;\n  }\n\n  public Object getMetadata() {\n    return metadata;\n  }\n\n  @Override\n  public int hashCode() {\n    final int prime = 31;\n    int result = 1;\n    result = prime * result + ((metadata == null) ? 0 : metadata.hashCode());\n    result = prime * result + ((tag == null) ? 0 : tag.hashCode());\n    return result;\n  }\n\n  @Override\n  public boolean equals(Object obj) {\n    if (this == obj) return true;\n    if (obj == null) return false;\n    if (getClass() != obj.getClass()) return false;\n    SmtStrategy other = (SmtStrategy) obj;\n    if (metadata == null) {\n      if (other.metadata != null) return false;\n    } else if (!metadata.equals(other.metadata)) return false;\n    if (tag != other.tag) return false;\n    return true;\n  }\n\n  @Override\n  public String toString() {\n    return \"SmtStrategy [tag=\" + tag + \", metadata=\" + metadata + \"]\";\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/smt/YicesProcessFactory.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2020-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.smt;\n\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class YicesProcessFactory implements ExternalSolverProcessFactory {\n\n  private static YicesProcessFactory instance;\n\n  private YicesProcessFactory() {\n    Util.assertBinaryOnPath(\"yices-smt2\");\n  }\n\n  public static YicesProcessFactory get() {\n    if (instance == null) {\n      synchronized (YicesProcessFactory.class) {\n        if (instance == null) {\n          instance = new YicesProcessFactory();\n        }\n      }\n    }\n    return instance;\n  }\n\n  @Override\n  public Process newProcess(boolean incremental) throws IOException {\n    List<String> command = new ArrayList<>();\n    command.add(\"yices-smt2\");\n    if (incremental) {\n      command.add(\"--incremental\");\n    }\n    return new ProcessBuilder(command).redirectErrorStream(true).start();\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/smt/Z3ProcessFactory.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.smt;\n\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport java.io.IOException;\n\npublic class Z3ProcessFactory implements ExternalSolverProcessFactory {\n\n  private static Z3ProcessFactory instance;\n\n  private Z3ProcessFactory() {\n    Util.assertBinaryOnPath(\"z3\");\n  }\n\n  public static Z3ProcessFactory get() {\n    if (instance == null) {\n      synchronized (Z3ProcessFactory.class) {\n        if (instance == null) {\n          instance = new Z3ProcessFactory();\n        }\n      }\n    }\n    return instance;\n  }\n\n  @Override\n  public Process newProcess(boolean incremental) throws IOException {\n    return new ProcessBuilder(\"z3\", \"-in\", \"-smt2\").redirectErrorStream(true).start();\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/symbols/AbstractSymbol.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.symbols;\n\nabstract class AbstractSymbol implements Symbol {\n\n  private final String name;\n  private final int arity;\n\n  public AbstractSymbol(String name, int arity) {\n    this.name = name;\n    this.arity = arity;\n  }\n\n  @Override\n  public int getArity() {\n    return arity;\n  }\n\n  @Override\n  public String toString() {\n    return name;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/symbols/AbstractTypedSymbol.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.symbols;\n\nimport edu.harvard.seas.pl.formulog.types.FunctorType;\n\nabstract class AbstractTypedSymbol extends AbstractSymbol implements TypedSymbol {\n\n  private final FunctorType type;\n\n  public AbstractTypedSymbol(String name, int arity, FunctorType type) {\n    super(name, arity);\n    this.type = type;\n  }\n\n  @Override\n  public FunctorType getCompileTimeType() {\n    return type;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/symbols/AbstractWrappedRelationSymbol.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.symbols;\n\nimport edu.harvard.seas.pl.formulog.types.FunctorType;\n\npublic abstract class AbstractWrappedRelationSymbol<R extends RelationSymbol>\n    implements WrappedRelationSymbol<R> {\n\n  private final R baseSymbol;\n\n  public AbstractWrappedRelationSymbol(R baseSymbol) {\n    this.baseSymbol = baseSymbol;\n  }\n\n  @Override\n  public R getBaseSymbol() {\n    return baseSymbol;\n  }\n\n  @Override\n  public FunctorType getCompileTimeType() {\n    return baseSymbol.getCompileTimeType();\n  }\n\n  @Override\n  public int getArity() {\n    return baseSymbol.getArity();\n  }\n\n  @Override\n  public boolean isIdbSymbol() {\n    return baseSymbol.isIdbSymbol();\n  }\n\n  @Override\n  public boolean isBottomUp() {\n    return baseSymbol.isEdbSymbol();\n  }\n\n  @Override\n  public boolean isTopDown() {\n    return baseSymbol.isTopDown();\n  }\n\n  @Override\n  public boolean isDisk() {\n    return false;\n  }\n\n  @Override\n  public int hashCode() {\n    final int prime = 31;\n    int result = 1;\n    result = prime * result + ((baseSymbol == null) ? 0 : baseSymbol.hashCode());\n    return result;\n  }\n\n  @Override\n  public boolean equals(Object obj) {\n    if (this == obj) return true;\n    if (obj == null) return false;\n    if (getClass() != obj.getClass()) return false;\n    AbstractWrappedRelationSymbol<?> other = (AbstractWrappedRelationSymbol<?>) obj;\n    if (baseSymbol == null) {\n      if (other.baseSymbol != null) return false;\n    } else if (!baseSymbol.equals(other.baseSymbol)) return false;\n    return true;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/symbols/BuiltInConstructorGetterSymbol.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.symbols;\n\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.a;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.list;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.option;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.smt;\n\nimport edu.harvard.seas.pl.formulog.types.FunctorType;\nimport edu.harvard.seas.pl.formulog.types.Types.Type;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\npublic enum BuiltInConstructorGetterSymbol implements ConstructorSymbol {\n  CONS_1(\"#cons_1\", list(a), smt(a)),\n\n  CONS_2(\"#cons_2\", list(a), smt(list(a))),\n\n  SOME_1(\"#some_1\", option(a), smt(a));\n\n  private final String name;\n  private final FunctorType type;\n\n  private BuiltInConstructorGetterSymbol(String name, Type... types) {\n    this.name = name;\n    List<Type> argTypes = new ArrayList<>(Arrays.asList(types));\n    Type retType = argTypes.remove(types.length - 1);\n    type = new FunctorType(argTypes, retType);\n  }\n\n  @Override\n  public int getArity() {\n    return 1;\n  }\n\n  @Override\n  public FunctorType getCompileTimeType() {\n    return type;\n  }\n\n  @Override\n  public String toString() {\n    return name;\n  }\n\n  @Override\n  public ConstructorSymbolType getConstructorSymbolType() {\n    return ConstructorSymbolType.SOLVER_CONSTRUCTOR_GETTER;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/symbols/BuiltInConstructorSymbol.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.symbols;\n\nimport static edu.harvard.seas.pl.formulog.symbols.ConstructorSymbolType.SOLVER_EXPR;\nimport static edu.harvard.seas.pl.formulog.symbols.ConstructorSymbolType.VANILLA_CONSTRUCTOR;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.a;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.array;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.b;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.bool;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.bv;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.cmp;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.fp;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.int_;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.list;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.option;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.smt;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.smtPattern;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.smtWrappedVar;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.string;\n\nimport edu.harvard.seas.pl.formulog.types.FunctorType;\nimport edu.harvard.seas.pl.formulog.types.Types.Type;\n\npublic enum BuiltInConstructorSymbol implements ConstructorSymbol {\n\n  // Lists\n\n  NIL(\"nil\", 0, VANILLA_CONSTRUCTOR),\n\n  CONS(\"cons\", 2, VANILLA_CONSTRUCTOR),\n\n  // Options\n\n  NONE(\"none\", 0, VANILLA_CONSTRUCTOR),\n\n  SOME(\"some\", 1, VANILLA_CONSTRUCTOR),\n\n  // Comparisons\n\n  CMP_LT(\"cmp_lt\", 0, VANILLA_CONSTRUCTOR),\n\n  CMP_EQ(\"cmp_eq\", 0, VANILLA_CONSTRUCTOR),\n\n  CMP_GT(\"cmp_gt\", 0, VANILLA_CONSTRUCTOR),\n\n  // Constraints\n\n  SMT_NOT(\"smt_not\", 1, SOLVER_EXPR),\n\n  SMT_AND(\"smt_and\", 2, SOLVER_EXPR),\n\n  SMT_OR(\"smt_or\", 2, SOLVER_EXPR),\n\n  SMT_IMP(\"smt_imp\", 2, SOLVER_EXPR),\n\n  SMT_ITE(\"smt_ite\", 3, SOLVER_EXPR),\n\n  SMT_EXISTS(\"smt_exists\", 3, SOLVER_EXPR),\n\n  SMT_FORALL(\"smt_forall\", 3, SOLVER_EXPR),\n\n  // Bit vectors\n\n  BV_NEG(\"bv_neg\", 1, SOLVER_EXPR),\n\n  BV_ADD(\"bv_add\", 2, SOLVER_EXPR),\n\n  BV_SUB(\"bv_sub\", 2, SOLVER_EXPR),\n\n  BV_MUL(\"bv_mul\", 2, SOLVER_EXPR),\n\n  BV_SDIV(\"bv_sdiv\", 2, SOLVER_EXPR),\n\n  BV_SREM(\"bv_srem\", 2, SOLVER_EXPR),\n\n  BV_UDIV(\"bv_udiv\", 2, SOLVER_EXPR),\n\n  BV_UREM(\"bv_urem\", 2, SOLVER_EXPR),\n\n  BV_AND(\"bv_and\", 2, SOLVER_EXPR),\n\n  BV_OR(\"bv_or\", 2, SOLVER_EXPR),\n\n  BV_XOR(\"bv_xor\", 2, SOLVER_EXPR),\n\n  BV_SHL(\"bv_shl\", 2, SOLVER_EXPR),\n\n  BV_ASHR(\"bv_ashr\", 2, SOLVER_EXPR),\n\n  BV_LSHR(\"bv_lshr\", 2, SOLVER_EXPR),\n\n  // Floating point\n\n  FP_NEG(\"fp_neg\", 1, SOLVER_EXPR),\n\n  FP_ADD(\"fp_add\", 2, SOLVER_EXPR),\n\n  FP_SUB(\"fp_sub\", 2, SOLVER_EXPR),\n\n  FP_MUL(\"fp_mul\", 2, SOLVER_EXPR),\n\n  FP_DIV(\"fp_div\", 2, SOLVER_EXPR),\n\n  FP_REM(\"fp_rem\", 2, SOLVER_EXPR),\n\n  // Arrays\n\n  ARRAY_STORE(\"array_store\", 3, SOLVER_EXPR),\n\n  ARRAY_CONST(\"array_const\", 1, SOLVER_EXPR),\n\n  // Strings\n\n  STR_CONCAT(\"str_concat\", 2, SOLVER_EXPR),\n\n  STR_LEN(\"str_len\", 1, SOLVER_EXPR),\n\n  STR_SUBSTR(\"str_substr\", 3, SOLVER_EXPR),\n\n  STR_INDEXOF(\"str_indexof\", 3, SOLVER_EXPR),\n\n  STR_AT(\"str_at\", 2, SOLVER_EXPR),\n\n  STR_CONTAINS(\"str_contains\", 2, SOLVER_EXPR),\n\n  STR_PREFIXOF(\"str_prefixof\", 2, SOLVER_EXPR),\n\n  STR_SUFFIXOF(\"str_suffixof\", 2, SOLVER_EXPR),\n\n  STR_REPLACE(\"str_replace\", 3, SOLVER_EXPR),\n\n  // Ints\n\n  INT_CONST(\"int_const\", 1, SOLVER_EXPR),\n\n  INT_BIG_CONST(\"int_big_const\", 1, SOLVER_EXPR),\n\n  INT_NEG(\"int_neg\", 1, SOLVER_EXPR),\n\n  INT_SUB(\"int_sub\", 2, SOLVER_EXPR),\n\n  INT_ADD(\"int_add\", 2, SOLVER_EXPR),\n\n  INT_MUL(\"int_mul\", 2, SOLVER_EXPR),\n\n  INT_DIV(\"int_div\", 2, SOLVER_EXPR),\n\n  INT_MOD(\"int_mod\", 2, SOLVER_EXPR),\n\n  INT_ABS(\"int_abs\", 1, SOLVER_EXPR),\n\n  INT_LE(\"int_le\", 2, SOLVER_EXPR),\n\n  INT_LT(\"int_lt\", 2, SOLVER_EXPR),\n\n  INT_GE(\"int_ge\", 2, SOLVER_EXPR),\n\n  INT_GT(\"int_gt\", 2, SOLVER_EXPR),\n\n  // Stuff for type checking formulas\n\n  ENTER_FORMULA(\"enter_formula\", 1, VANILLA_CONSTRUCTOR),\n\n  EXIT_FORMULA(\"exit_formula\", 1, VANILLA_CONSTRUCTOR),\n  ;\n\n  private final String name;\n  private final int arity;\n  private final ConstructorSymbolType st;\n\n  private BuiltInConstructorSymbol(String name, int arity, ConstructorSymbolType st) {\n    this.name = name;\n    this.arity = arity;\n    this.st = st;\n  }\n\n  @Override\n  public int getArity() {\n    return arity;\n  }\n\n  private FunctorType makeType(Type... types) {\n    assert types.length == arity + 1;\n    return new FunctorType(types);\n  }\n\n  @Override\n  public FunctorType getCompileTimeType() {\n    switch (this) {\n      case CMP_EQ:\n      case CMP_GT:\n      case CMP_LT:\n        return makeType(cmp);\n      case NIL:\n        return makeType(list(a));\n      case CONS:\n        return makeType(a, list(a), list(a));\n      case SMT_AND:\n      case SMT_OR:\n      case SMT_IMP:\n        return makeType(smt(bool), smt(bool), smt(bool));\n      case SMT_ITE:\n        return makeType(smt(bool), smt(a), smt(a), smt(a));\n      case SMT_NOT:\n        return makeType(smt(bool), smt(bool));\n      case SMT_EXISTS:\n      case SMT_FORALL:\n        return makeType(list(smtWrappedVar), smt(bool), list(list(smtPattern)), smt(bool));\n      case NONE:\n        return makeType(option(a));\n      case SOME:\n        return makeType(a, option(a));\n      case BV_ADD:\n      case BV_AND:\n      case BV_MUL:\n      case BV_OR:\n      case BV_SDIV:\n      case BV_SREM:\n      case BV_UDIV:\n      case BV_UREM:\n      case BV_SUB:\n      case BV_XOR:\n      case BV_SHL:\n      case BV_ASHR:\n      case BV_LSHR:\n        return makeType(smt(bv(a)), smt(bv(a)), smt(bv(a)));\n      case BV_NEG:\n        return makeType(smt(bv(a)), smt(bv(a)));\n      case FP_ADD:\n      case FP_DIV:\n      case FP_REM:\n      case FP_SUB:\n      case FP_MUL:\n        return makeType(smt(fp(a, b)), smt(fp(a, b)), smt(fp(a, b)));\n      case FP_NEG:\n        return makeType(smt(fp(a, b)), smt(fp(a, b)));\n      case ARRAY_STORE:\n        return makeType(smt(array(a, b)), smt(a), smt(b), smt(array(a, b)));\n      case ARRAY_CONST:\n        return makeType(smt(b), smt(array(a, b)));\n      case STR_AT:\n        return makeType(smt(string), smt(int_), smt(string));\n      case STR_CONCAT:\n        return makeType(smt(string), smt(string), smt(string));\n      case STR_CONTAINS:\n        return makeType(smt(string), smt(string), smt(bool));\n      case STR_INDEXOF:\n        return makeType(smt(string), smt(string), smt(int_), smt(int_));\n      case STR_LEN:\n        return makeType(smt(string), smt(int_));\n      case STR_PREFIXOF:\n        return makeType(smt(string), smt(string), smt(bool));\n      case STR_REPLACE:\n        return makeType(smt(string), smt(string), smt(string), smt(string));\n      case STR_SUBSTR:\n        return makeType(smt(string), smt(int_), smt(int_), smt(string));\n      case STR_SUFFIXOF:\n        return makeType(smt(string), smt(string), smt(bool));\n      case INT_ABS:\n      case INT_NEG:\n        return makeType(smt(int_), smt(int_));\n      case INT_BIG_CONST:\n        return makeType(bv(64), smt(int_));\n      case INT_CONST:\n        return makeType(bv(32), smt(int_));\n      case INT_GE:\n      case INT_GT:\n      case INT_LE:\n      case INT_LT:\n        return makeType(smt(int_), smt(int_), smt(bool));\n      case INT_ADD:\n      case INT_MUL:\n      case INT_MOD:\n      case INT_SUB:\n      case INT_DIV:\n        return makeType(smt(int_), smt(int_), smt(int_));\n      case ENTER_FORMULA:\n        return makeType(smt(a), smt(a));\n      case EXIT_FORMULA:\n        return makeType(a, a);\n    }\n    throw new AssertionError(\"impossible\");\n  }\n\n  @Override\n  public ConstructorSymbolType getConstructorSymbolType() {\n    return st;\n  }\n\n  @Override\n  public String toString() {\n    return name;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/symbols/BuiltInConstructorTesterSymbol.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.symbols;\n\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.a;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.bool;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.cmp;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.list;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.option;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.smt;\n\nimport edu.harvard.seas.pl.formulog.types.FunctorType;\nimport edu.harvard.seas.pl.formulog.types.Types.Type;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\npublic enum BuiltInConstructorTesterSymbol implements ConstructorSymbol {\n  IS_CMP_LT(\"#is_cmp_lt\", cmp, smt(bool)),\n\n  IS_CMP_EQ(\"#is_cmp_eq\", cmp, smt(bool)),\n\n  IS_CMP_GT(\"#is_cmp_gt\", cmp, smt(bool)),\n\n  IS_NIL(\"#is_nil\", list(a), smt(bool)),\n\n  IS_CONS(\"#is_cons\", list(a), smt(bool)),\n\n  IS_NONE(\"#is_none\", option(a), smt(bool)),\n\n  IS_SOME(\"#is_some\", option(a), smt(bool)),\n  ;\n\n  private final FunctorType type;\n  private final String name;\n\n  private BuiltInConstructorTesterSymbol(String name, Type... types) {\n    this.name = name;\n    List<Type> argTypes = new ArrayList<>(Arrays.asList(types));\n    Type retType = argTypes.remove(types.length - 1);\n    type = new FunctorType(argTypes, retType);\n  }\n\n  @Override\n  public int getArity() {\n    return 1;\n  }\n\n  @Override\n  public ConstructorSymbolType getConstructorSymbolType() {\n    return ConstructorSymbolType.SOLVER_CONSTRUCTOR_TESTER;\n  }\n\n  @Override\n  public FunctorType getCompileTimeType() {\n    return type;\n  }\n\n  @Override\n  public String toString() {\n    return name;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/symbols/BuiltInFunctionSymbol.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.symbols;\n\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.a;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.bool;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.cmp;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.fp32;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.fp64;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.i32;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.i64;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.list;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.model;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.opaqueSet;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.option;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.pair;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.smt;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.string;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.sym;\n\nimport edu.harvard.seas.pl.formulog.types.FunctorType;\n\npublic enum BuiltInFunctionSymbol implements FunctionSymbol {\n\n  // i32 operations\n\n  I32_ADD(\"i32_add\", 2),\n\n  I32_SUB(\"i32_sub\", 2),\n\n  I32_MUL(\"i32_mul\", 2),\n\n  I32_SDIV(\"i32_sdiv\", 2),\n\n  I32_SREM(\"i32_srem\", 2),\n\n  I32_UDIV(\"i32_udiv\", 2),\n\n  I32_UREM(\"i32_urem\", 2),\n\n  I32_NEG(\"i32_neg\", 1),\n\n  I32_LT(\"i32_lt\", 2),\n\n  I32_LE(\"i32_le\", 2),\n\n  I32_GT(\"i32_gt\", 2),\n\n  I32_GE(\"i32_ge\", 2),\n\n  I32_AND(\"i32_and\", 2),\n\n  I32_OR(\"i32_or\", 2),\n\n  I32_XOR(\"i32_xor\", 2),\n\n  I32_SCMP(\"i32_scmp\", 2),\n\n  I32_UCMP(\"i32_ucmp\", 2),\n\n  I32_SHL(\"i32_shl\", 2),\n\n  I32_ASHR(\"i32_ashr\", 2),\n\n  I32_LSHR(\"i32_lshr\", 2),\n\n  // i64 operations\n\n  I64_ADD(\"i64_add\", 2),\n\n  I64_SUB(\"i64_sub\", 2),\n\n  I64_MUL(\"i64_mul\", 2),\n\n  I64_SDIV(\"i64_sdiv\", 2),\n\n  I64_SREM(\"i64_srem\", 2),\n\n  I64_UDIV(\"i64_udiv\", 2),\n\n  I64_UREM(\"i64_urem\", 2),\n\n  I64_NEG(\"i64_neg\", 1),\n\n  I64_LT(\"i64_lt\", 2),\n\n  I64_LE(\"i64_le\", 2),\n\n  I64_GT(\"i64_gt\", 2),\n\n  I64_GE(\"i64_ge\", 2),\n\n  I64_AND(\"i64_and\", 2),\n\n  I64_OR(\"i64_or\", 2),\n\n  I64_XOR(\"i64_xor\", 2),\n\n  I64_SCMP(\"i64_scmp\", 2),\n\n  I64_UCMP(\"i64_ucmp\", 2),\n\n  I64_SHL(\"i64_shl\", 2),\n\n  I64_ASHR(\"i64_ashr\", 2),\n\n  I64_LSHR(\"i64_lshr\", 2),\n\n  // fp32 operations\n\n  FP32_ADD(\"fp32_add\", 2),\n\n  FP32_SUB(\"fp32_sub\", 2),\n\n  FP32_MUL(\"fp32_mul\", 2),\n\n  FP32_DIV(\"fp32_div\", 2),\n\n  FP32_REM(\"fp32_rem\", 2),\n\n  FP32_NEG(\"fp32_neg\", 1),\n\n  FP32_LT(\"fp32_lt\", 2),\n\n  FP32_LE(\"fp32_le\", 2),\n\n  FP32_GT(\"fp32_gt\", 2),\n\n  FP32_GE(\"fp32_ge\", 2),\n\n  FP32_EQ(\"fp32_eq\", 2),\n\n  // fp64 operations\n\n  FP64_ADD(\"fp64_add\", 2),\n\n  FP64_SUB(\"fp64_sub\", 2),\n\n  FP64_MUL(\"fp64_mul\", 2),\n\n  FP64_DIV(\"fp64_div\", 2),\n\n  FP64_REM(\"fp64_rem\", 2),\n\n  FP64_NEG(\"fp64_neg\", 1),\n\n  FP64_LT(\"fp64_lt\", 2),\n\n  FP64_LE(\"fp64_le\", 2),\n\n  FP64_GT(\"fp64_gt\", 2),\n\n  FP64_GE(\"fp64_ge\", 2),\n\n  FP64_EQ(\"fp64_eq\", 2),\n\n  // Boolean operations\n\n  BEQ(\"beq\", 2),\n\n  BNEQ(\"bneq\", 2),\n\n  BNOT(\"bnot\", 1),\n\n  // String operations\n\n  STRING_CMP(\"string_cmp\", 2),\n\n  STRING_CONCAT(\"string_concat\", 2),\n\n  STRING_MATCHES(\"string_matches\", 2),\n\n  STRING_STARTS_WITH(\"string_starts_with\", 2),\n\n  TO_STRING(\"to_string\", 1),\n\n  STRING_TO_LIST(\"string_to_list\", 1),\n\n  LIST_TO_STRING(\"list_to_string\", 1),\n\n  CHAR_AT(\"char_at\", 2),\n\n  SUBSTRING(\"substring\", 3),\n\n  STRING_LENGTH(\"string_length\", 1),\n\n  // Constraint solving\n\n  IS_SAT(\"is_sat\", 1),\n\n  IS_VALID(\"is_valid\", 1),\n\n  IS_SAT_OPT(\"is_sat_opt\", 2),\n\n  GET_MODEL(\"get_model\", 2),\n\n  QUERY_MODEL(\"query_model\", 2),\n\n  IS_SET_SAT(\"is_set_sat\", 2),\n\n  // Opaque datatypes\n\n  OPAQUE_SET_EMPTY(\"opaque_set_empty\", 0),\n\n  OPAQUE_SET_PLUS(\"opaque_set_plus\", 2),\n\n  OPAQUE_SET_MINUS(\"opaque_set_minus\", 2),\n\n  OPAQUE_SET_UNION(\"opaque_set_union\", 2),\n\n  OPAQUE_SET_DIFF(\"opaque_set_diff\", 2),\n\n  OPAQUE_SET_CHOOSE(\"opaque_set_choose\", 1),\n\n  OPAQUE_SET_SIZE(\"opaque_set_size\", 1),\n\n  OPAQUE_SET_MEMBER(\"opaque_set_member\", 2),\n\n  OPAQUE_SET_SINGLETON(\"opaque_set_singleton\", 1),\n\n  OPAQUE_SET_SUBSET(\"opaque_set_subset\", 2),\n\n  OPAQUE_SET_FROM_LIST(\"opaque_set_from_list\", 1),\n\n  // Primitive conversion\n\n  i32ToI64(\"i32_to_i64\", 1),\n\n  i32ToFp32(\"i32_to_fp32\", 1),\n\n  i32ToFp64(\"i32_to_fp64\", 1),\n\n  i64ToI32(\"i64_to_i32\", 1),\n\n  i64ToFp32(\"i64_to_fp32\", 1),\n\n  i64ToFp64(\"i64_to_fp64\", 1),\n\n  fp32ToI32(\"fp32_to_i32\", 1),\n\n  fp32ToI64(\"fp32_to_i64\", 1),\n\n  fp32ToFp64(\"fp32_to_fp64\", 1),\n\n  fp64ToI32(\"fp64_to_i32\", 1),\n\n  fp64ToI64(\"fp64_to_i64\", 1),\n\n  fp64ToFp32(\"fp64_to_fp32\", 1),\n\n  stringToI32(\"string_to_i32\", 1),\n\n  stringToI64(\"string_to_i64\", 1),\n\n  // Debugging\n\n  PRINT(\"print\", 1),\n  ;\n\n  private final String name;\n  private final int arity;\n\n  BuiltInFunctionSymbol(String name, int arity) {\n    this.name = name;\n    this.arity = arity;\n  }\n\n  @Override\n  public int getArity() {\n    return arity;\n  }\n\n  @Override\n  public FunctorType getCompileTimeType() {\n    switch (this) {\n      case BEQ:\n      case BNEQ:\n        return new FunctorType(a, a, bool);\n      case BNOT:\n        return new FunctorType(bool, bool);\n      case FP32_NEG:\n        return new FunctorType(fp32, fp32);\n      case FP32_ADD:\n      case FP32_DIV:\n      case FP32_MUL:\n      case FP32_REM:\n      case FP32_SUB:\n        return new FunctorType(fp32, fp32, fp32);\n      case FP32_EQ:\n      case FP32_GE:\n      case FP32_GT:\n      case FP32_LE:\n      case FP32_LT:\n        return new FunctorType(fp32, fp32, bool);\n      case FP64_NEG:\n        return new FunctorType(fp64, fp64);\n      case FP64_ADD:\n      case FP64_DIV:\n      case FP64_MUL:\n      case FP64_REM:\n      case FP64_SUB:\n        return new FunctorType(fp64, fp64, fp64);\n      case FP64_EQ:\n      case FP64_GE:\n      case FP64_GT:\n      case FP64_LE:\n      case FP64_LT:\n        return new FunctorType(fp64, fp64, bool);\n      case GET_MODEL:\n        return new FunctorType(list(smt(bool)), option(i32), option(model));\n      case I32_NEG:\n        return new FunctorType(i32, i32);\n      case I32_ADD:\n      case I32_AND:\n      case I32_SDIV:\n      case I32_MUL:\n      case I32_OR:\n      case I32_SREM:\n      case I32_UDIV:\n      case I32_UREM:\n      case I32_SUB:\n      case I32_XOR:\n      case I32_SHL:\n      case I32_ASHR:\n      case I32_LSHR:\n        return new FunctorType(i32, i32, i32);\n      case I32_GE:\n      case I32_GT:\n      case I32_LE:\n      case I32_LT:\n        return new FunctorType(i32, i32, bool);\n      case I32_SCMP:\n      case I32_UCMP:\n        return new FunctorType(i32, i32, cmp);\n      case I64_NEG:\n        return new FunctorType(i64, i64);\n      case I64_ADD:\n      case I64_AND:\n      case I64_SDIV:\n      case I64_MUL:\n      case I64_OR:\n      case I64_SREM:\n      case I64_UDIV:\n      case I64_UREM:\n      case I64_SUB:\n      case I64_XOR:\n      case I64_SHL:\n      case I64_ASHR:\n      case I64_LSHR:\n        return new FunctorType(i64, i64, i64);\n      case I64_GE:\n      case I64_GT:\n      case I64_LE:\n      case I64_LT:\n        return new FunctorType(i64, i64, bool);\n      case I64_SCMP:\n      case I64_UCMP:\n        return new FunctorType(i64, i64, cmp);\n      case IS_SAT:\n      case IS_VALID:\n        return new FunctorType(smt(bool), bool);\n      case IS_SET_SAT:\n        return new FunctorType(opaqueSet(smt(bool)), option(i32), option(bool));\n      case IS_SAT_OPT:\n        return new FunctorType(list(smt(bool)), option(i32), option(bool));\n      case PRINT:\n        return new FunctorType(a, bool);\n      case QUERY_MODEL:\n        return new FunctorType(sym(a), model, option(a));\n      case STRING_CONCAT:\n        return new FunctorType(string, string, string);\n      case STRING_CMP:\n        return new FunctorType(string, string, cmp);\n      case STRING_MATCHES:\n        return new FunctorType(string, string, bool);\n      case TO_STRING:\n        return new FunctorType(a, string);\n      case STRING_STARTS_WITH:\n        return new FunctorType(string, string, bool);\n      case STRING_TO_LIST:\n        return new FunctorType(string, list(i32));\n      case LIST_TO_STRING:\n        return new FunctorType(list(i32), string);\n      case CHAR_AT:\n        return new FunctorType(string, i32, option(i32));\n      case SUBSTRING:\n        return new FunctorType(string, i32, i32, option(string));\n      case STRING_LENGTH:\n        return new FunctorType(string, i32);\n      case fp32ToFp64:\n        return new FunctorType(fp32, fp64);\n      case fp32ToI32:\n        return new FunctorType(fp32, i32);\n      case fp32ToI64:\n        return new FunctorType(fp32, i64);\n      case fp64ToFp32:\n        return new FunctorType(fp64, fp32);\n      case fp64ToI32:\n        return new FunctorType(fp64, i32);\n      case fp64ToI64:\n        return new FunctorType(fp64, i64);\n      case i32ToFp32:\n        return new FunctorType(i32, fp32);\n      case i32ToFp64:\n        return new FunctorType(i32, fp64);\n      case i32ToI64:\n        return new FunctorType(i32, i64);\n      case i64ToFp32:\n        return new FunctorType(i64, fp32);\n      case i64ToFp64:\n        return new FunctorType(i64, fp64);\n      case i64ToI32:\n        return new FunctorType(i64, i32);\n      case stringToI32:\n        return new FunctorType(string, option(i32));\n      case stringToI64:\n        return new FunctorType(string, option(i64));\n      case OPAQUE_SET_CHOOSE:\n        return new FunctorType(opaqueSet(a), option(pair(a, opaqueSet(a))));\n      case OPAQUE_SET_DIFF:\n        return new FunctorType(opaqueSet(a), opaqueSet(a), opaqueSet(a));\n      case OPAQUE_SET_EMPTY:\n        return new FunctorType(opaqueSet(a));\n      case OPAQUE_SET_PLUS:\n        return new FunctorType(a, opaqueSet(a), opaqueSet(a));\n      case OPAQUE_SET_MINUS:\n        return new FunctorType(a, opaqueSet(a), opaqueSet(a));\n      case OPAQUE_SET_UNION:\n        return new FunctorType(opaqueSet(a), opaqueSet(a), opaqueSet(a));\n      case OPAQUE_SET_SIZE:\n        return new FunctorType(opaqueSet(a), i32);\n      case OPAQUE_SET_MEMBER:\n        return new FunctorType(a, opaqueSet(a), bool);\n      case OPAQUE_SET_SINGLETON:\n        return new FunctorType(a, opaqueSet(a));\n      case OPAQUE_SET_SUBSET:\n        return new FunctorType(opaqueSet(a), opaqueSet(a), bool);\n      case OPAQUE_SET_FROM_LIST:\n        return new FunctorType(list(a), opaqueSet(a));\n    }\n    throw new AssertionError(\"impossible\");\n  }\n\n  @Override\n  public String toString() {\n    return name;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/symbols/BuiltInTypeSymbol.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.symbols;\n\npublic enum BuiltInTypeSymbol implements TypeSymbol {\n  BOOL_TYPE(\"bool\", 0),\n\n  LIST_TYPE(\"list\", 1),\n\n  OPTION_TYPE(\"option\", 1),\n\n  CMP_TYPE(\"cmp\", 0),\n\n  STRING_TYPE(\"string\", 0),\n\n  SMT_TYPE(\"smt\", 1),\n\n  SYM_TYPE(\"sym\", 1),\n\n  ARRAY_TYPE(\"array\", 2),\n\n  MODEL_TYPE(\"model\", 0),\n\n  INT_TYPE(\"int\", 0),\n\n  BV(\"bv\", 1),\n\n  FP(\"fp\", 2),\n\n  OPAQUE_SET(\"opaque_set\", 1),\n\n  SMT_PATTERN_TYPE(\"smt_pattern\", 0),\n\n  SMT_WRAPPED_VAR_TYPE(\"smt_wrapped_var\", 0),\n  ;\n\n  private final String name;\n  private final int arity;\n\n  private BuiltInTypeSymbol(String name, int arity) {\n    this.name = name;\n    this.arity = arity;\n  }\n\n  @Override\n  public int getArity() {\n    return arity;\n  }\n\n  @Override\n  public TypeSymbolType getTypeSymbolType() {\n    return TypeSymbolType.NORMAL_TYPE;\n  }\n\n  @Override\n  public String toString() {\n    return name;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/symbols/ConstructorSymbol.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.symbols;\n\npublic interface ConstructorSymbol extends TypedSymbol {\n\n  ConstructorSymbolType getConstructorSymbolType();\n\n  default boolean isSolverConstructorSymbol() {\n    switch (getConstructorSymbolType()) {\n      case SOLVER_CONSTRUCTOR_GETTER:\n      case SOLVER_CONSTRUCTOR_TESTER:\n      case SOLVER_EXPR:\n      case SOLVER_UNINTERPRETED_FUNCTION:\n      case INDEX_CONSTRUCTOR:\n      case VANILLA_CONSTRUCTOR:\n        return false;\n    }\n    throw new AssertionError(\"impossible\");\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/symbols/ConstructorSymbolImpl.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.symbols;\n\nimport edu.harvard.seas.pl.formulog.types.FunctorType;\n\nclass ConstructorSymbolImpl extends AbstractTypedSymbol implements ConstructorSymbol {\n\n  private final ConstructorSymbolType symType;\n\n  public ConstructorSymbolImpl(\n      String name, int arity, ConstructorSymbolType symType, FunctorType type) {\n    super(name, arity, type);\n    this.symType = symType;\n  }\n\n  @Override\n  public ConstructorSymbolType getConstructorSymbolType() {\n    return symType;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/symbols/ConstructorSymbolType.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.symbols;\n\npublic enum ConstructorSymbolType {\n  SOLVER_UNINTERPRETED_FUNCTION,\n\n  VANILLA_CONSTRUCTOR,\n\n  SOLVER_EXPR,\n\n  SOLVER_CONSTRUCTOR_GETTER,\n\n  SOLVER_CONSTRUCTOR_TESTER,\n\n  INDEX_CONSTRUCTOR,\n  ;\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/symbols/FunctionSymbol.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.symbols;\n\npublic interface FunctionSymbol extends TypedSymbol {}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/symbols/GlobalSymbolManager.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.symbols;\n\nimport edu.harvard.seas.pl.formulog.symbols.parameterized.BuiltInConstructorSymbolBase;\nimport edu.harvard.seas.pl.formulog.symbols.parameterized.Param;\nimport edu.harvard.seas.pl.formulog.symbols.parameterized.ParameterizedConstructorSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.parameterized.ParameterizedSymbol;\nimport edu.harvard.seas.pl.formulog.types.FunctorType;\nimport edu.harvard.seas.pl.formulog.types.Types.AlgebraicDataType;\nimport edu.harvard.seas.pl.formulog.types.Types.AlgebraicDataType.ConstructorScheme;\nimport edu.harvard.seas.pl.formulog.types.Types.Type;\nimport edu.harvard.seas.pl.formulog.types.Types.TypeVar;\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\n\npublic final class GlobalSymbolManager {\n\n  private GlobalSymbolManager() {\n    throw new AssertionError(\"impossible\");\n  }\n\n  private static boolean initialized = false;\n\n  private static final Map<String, Symbol> memo = new ConcurrentHashMap<>();\n  private static final Set<TypeSymbol> typeSymbols = Util.concurrentSet();\n\n  public static boolean hasName(String name) {\n    checkInitialized();\n    return memo.containsKey(name);\n  }\n\n  public static Symbol lookup(String name) {\n    return lookup(name, Collections.emptyList());\n  }\n\n  public static Symbol lookup(String name, Param... params) {\n    return lookup(name, Arrays.asList(params));\n  }\n\n  public static Symbol lookup(String name, List<Param> params) {\n    checkInitialized();\n    if (!hasName(name)) {\n      throw new IllegalArgumentException(\"Unrecognized name: \" + name);\n    }\n    Symbol sym = memo.get(name);\n    assert sym != null;\n    if (sym instanceof ParameterizedSymbol) {\n      sym = ((ParameterizedSymbol) sym).copyWithNewArgs(params);\n    } else if (!params.isEmpty()) {\n      throw new IllegalArgumentException(\n          \"Cannot supply parameters to non-parameterized symbol: \" + sym);\n    }\n    return sym;\n  }\n\n  public static ParameterizedConstructorSymbol getParameterizedSymbol(\n      BuiltInConstructorSymbolBase base, List<Param> params) {\n    return getParameterizedSymbol(base).copyWithNewArgs(params);\n  }\n\n  public static ParameterizedConstructorSymbol getParameterizedSymbol(\n      BuiltInConstructorSymbolBase base) {\n    initialize();\n    return getParameterizedSymbolInternal(base);\n  }\n\n  private static ParameterizedConstructorSymbol getParameterizedSymbolInternal(\n      BuiltInConstructorSymbolBase base) {\n    List<Param> params = Param.wildCards(base.getNumParams());\n    return ParameterizedConstructorSymbol.mk(base, params);\n  }\n\n  private static void checkInitialized() {\n    if (!initialized) {\n      initialize();\n    }\n  }\n\n  private static synchronized void initialize() {\n    if (initialized) {\n      return;\n    }\n    register(BuiltInTypeSymbol.values());\n    register(BuiltInConstructorSymbol.values());\n    register(BuiltInConstructorTesterSymbol.values());\n    register(BuiltInConstructorGetterSymbol.values());\n    register(BuiltInConstructorSymbolBase.values());\n    register(BuiltInFunctionSymbol.values());\n    initialized = true;\n  }\n\n  private static void register(Symbol[] symbols) {\n    for (Symbol sym : symbols) {\n      register(sym);\n    }\n  }\n\n  private static void register(Symbol sym) {\n    if (sym instanceof TypeSymbol) {\n      typeSymbols.add((TypeSymbol) sym);\n    }\n    Symbol other = memo.putIfAbsent(sym.toString(), sym);\n    assert other == null;\n  }\n\n  private static void register(BuiltInConstructorSymbolBase[] bases) {\n    for (BuiltInConstructorSymbolBase base : bases) {\n      ParameterizedConstructorSymbol sym = getParameterizedSymbolInternal(base);\n      Symbol other = memo.putIfAbsent(base.toString(), sym);\n      assert other == null;\n    }\n  }\n\n  private static TypeSymbol createTypeSymbol(String name, int arity, TypeSymbolType symType) {\n    initialize();\n    TypeSymbol sym = new TypeSymbolImpl(name, arity, symType);\n    register(sym);\n    return sym;\n  }\n\n  private static ConstructorSymbol createConstructorSymbol(\n      String name, int arity, ConstructorSymbolType symType, FunctorType type) {\n    initialize();\n    ConstructorSymbol sym = new ConstructorSymbolImpl(name, arity, symType, type);\n    register(sym);\n    return sym;\n  }\n\n  public static Set<TypeSymbol> getTypeSymbols() {\n    initialize();\n    return Collections.unmodifiableSet(typeSymbols);\n  }\n\n  private static final Map<Integer, TupleSymbol> tupleSymbolMemo = new ConcurrentHashMap<>();\n  private static final Map<Integer, TypeSymbol> tupleTypeSymbolMemo = new ConcurrentHashMap<>();\n\n  public static TupleSymbol lookupTupleSymbol(int arity) {\n    instantiateTuple(arity);\n    return tupleSymbolMemo.get(arity);\n  }\n\n  public static TypeSymbol lookupTupleTypeSymbol(int arity) {\n    instantiateTuple(arity);\n    return tupleTypeSymbolMemo.get(arity);\n  }\n\n  private static void instantiateTuple(int arity) {\n    TupleSymbol tupSym = tupleSymbolMemo.get(arity);\n    if (tupSym != null) {\n      return;\n    }\n    TypeSymbol typeSym = createTypeSymbol(\"tuple_type$\" + arity, arity, TypeSymbolType.NORMAL_TYPE);\n    List<Type> typeArgs = new ArrayList<>();\n    List<TypeVar> typeVars = new ArrayList<>();\n    for (int i = 0; i < arity; ++i) {\n      TypeVar x = TypeVar.fresh();\n      typeArgs.add(x);\n      typeVars.add(x);\n    }\n    AlgebraicDataType type = AlgebraicDataType.make(typeSym, typeArgs);\n    List<ConstructorSymbol> getters = new ArrayList<>();\n    int i = 0;\n    for (Type ty : typeArgs) {\n      String getter = \"#_tuple\" + arity + \"_\" + (i + 1);\n      FunctorType ft = new FunctorType(type, ty);\n      getters.add(\n          createConstructorSymbol(\n              getter, arity, ConstructorSymbolType.SOLVER_CONSTRUCTOR_GETTER, ft));\n      ++i;\n    }\n    FunctorType ctorTy = new FunctorType(typeArgs, type);\n    tupSym = new TupleSymbol(arity, ctorTy);\n    ConstructorScheme cs = new ConstructorScheme(tupSym, typeArgs, getters);\n    AlgebraicDataType.setConstructors(typeSym, typeVars, Collections.singleton(cs));\n    tupleSymbolMemo.put(arity, tupSym);\n    tupleTypeSymbolMemo.put(arity, typeSym);\n  }\n\n  public static class TupleSymbol implements ConstructorSymbol {\n\n    private final int arity;\n    private final FunctorType type;\n\n    private TupleSymbol(int arity, FunctorType type) {\n      this.arity = arity;\n      this.type = type;\n    }\n\n    @Override\n    public FunctorType getCompileTimeType() {\n      return type;\n    }\n\n    @Override\n    public int getArity() {\n      return arity;\n    }\n\n    @Override\n    public String toString() {\n      return \"tuple$\" + arity;\n    }\n\n    @Override\n    public ConstructorSymbolType getConstructorSymbolType() {\n      return ConstructorSymbolType.VANILLA_CONSTRUCTOR;\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/symbols/MutableRelationSymbol.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.symbols;\n\npublic interface MutableRelationSymbol extends RelationSymbol {\n\n  void setTopDown();\n\n  void setBottomUp();\n\n  void setDisk();\n\n  void setEdb();\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/symbols/PredicateFunctionSymbol.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.symbols;\n\nimport edu.harvard.seas.pl.formulog.ast.BindingType;\nimport edu.harvard.seas.pl.formulog.db.BindingTypeArrayWrapper;\nimport edu.harvard.seas.pl.formulog.types.BuiltInTypes;\nimport edu.harvard.seas.pl.formulog.types.FunctorType;\nimport edu.harvard.seas.pl.formulog.types.Types.AlgebraicDataType;\nimport edu.harvard.seas.pl.formulog.types.Types.Type;\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\npublic class PredicateFunctionSymbol implements FunctionSymbol {\n\n  private static final Map<RelationSymbol, Map<BindingTypeArrayWrapper, PredicateFunctionSymbol>>\n      memo = new HashMap<>();\n\n  private static final Map<RelationSymbol, PredicateFunctionSymbol> placeholders = new HashMap<>();\n\n  public static synchronized PredicateFunctionSymbol create(\n      RelationSymbol predSym, BindingType[] bindings, SymbolManager sm) {\n    Map<BindingTypeArrayWrapper, PredicateFunctionSymbol> m =\n        Util.lookupOrCreate(memo, predSym, () -> new HashMap<>());\n    BindingTypeArrayWrapper key = new BindingTypeArrayWrapper(bindings);\n    PredicateFunctionSymbol funcSym = m.get(key);\n    if (funcSym == null) {\n      funcSym = createNew(predSym, bindings, sm);\n      PredicateFunctionSymbol f2 = m.put(key, funcSym);\n      assert f2 == null;\n    }\n    return funcSym;\n  }\n\n  public static synchronized PredicateFunctionSymbol createPlaceholder(\n      RelationSymbol predSym, SymbolManager sm) {\n    PredicateFunctionSymbol sym =\n        Util.lookupOrCreate(\n            placeholders,\n            predSym,\n            () -> new PredicateFunctionSymbol(predSym, null, predSym.getCompileTimeType()));\n    sm.registerSymbol(sym);\n    return sym;\n  }\n\n  private static PredicateFunctionSymbol createNew(\n      RelationSymbol predSym, BindingType[] bindings, SymbolManager sm) {\n    assert predSym.getArity() == bindings.length;\n    List<Type> argTypes = new ArrayList<>();\n    List<Type> retTypes = new ArrayList<>();\n    FunctorType type = predSym.getCompileTimeType();\n    int i = 0;\n    for (Type ty : type.getArgTypes()) {\n      if (bindings[i].isBound()) {\n        argTypes.add(ty);\n      } else if (bindings[i].isFree()) {\n        retTypes.add(ty);\n      }\n      i++;\n    }\n    Type retType;\n    if (retTypes.isEmpty()) {\n      retType = BuiltInTypes.bool;\n    } else if (retTypes.size() == 1) {\n      retType = BuiltInTypes.list(retTypes.get(0));\n    } else {\n      TypeSymbol tupTypeSym = GlobalSymbolManager.lookupTupleTypeSymbol(retTypes.size());\n      retType = BuiltInTypes.list(AlgebraicDataType.make(tupTypeSym, retTypes));\n    }\n    type = new FunctorType(argTypes, retType);\n    PredicateFunctionSymbol funcSym = new PredicateFunctionSymbol(predSym, bindings, type);\n    sm.registerSymbol(funcSym);\n    return funcSym;\n  }\n\n  private final RelationSymbol predSymbol;\n  private final BindingType[] bindings;\n  private final FunctorType type;\n\n  private PredicateFunctionSymbol(\n      RelationSymbol predSymbol, BindingType[] bindings, FunctorType type) {\n    this.predSymbol = predSymbol;\n    this.bindings = bindings;\n    this.type = type;\n  }\n\n  @Override\n  public int getArity() {\n    return type.getArgTypes().size();\n  }\n\n  @Override\n  public FunctorType getCompileTimeType() {\n    return type;\n  }\n\n  public RelationSymbol getPredicateSymbol() {\n    return predSymbol;\n  }\n\n  public BindingType[] getBindings() {\n    return bindings;\n  }\n\n  @Override\n  public String toString() {\n    String s = predSymbol.toString();\n    if (bindings != null) {\n      if (bindings.length > 0) {\n        s += \"<\";\n        for (int i = 0; i < bindings.length; ++i) {\n          s += bindings[i];\n          if (i < bindings.length - 1) {\n            s += \",\";\n          }\n        }\n        s += \">\";\n      }\n    } else {\n      s += \"?\";\n    }\n    return s + \"?\";\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/symbols/RecordSymbol.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.symbols;\n\nimport java.util.List;\n\npublic interface RecordSymbol extends ConstructorSymbol {\n\n  List<FunctionSymbol> getLabels();\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/symbols/RelationSymbol.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.symbols;\n\npublic interface RelationSymbol extends TypedSymbol {\n\n  boolean isIdbSymbol();\n\n  default boolean isEdbSymbol() {\n    return !isIdbSymbol();\n  }\n\n  boolean isDisk();\n\n  boolean isBottomUp();\n\n  boolean isTopDown();\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/symbols/Symbol.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.symbols;\n\npublic interface Symbol {\n\n  int getArity();\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/symbols/SymbolComparator.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.symbols;\n\nimport java.util.Comparator;\n\npublic enum SymbolComparator implements Comparator<Symbol> {\n  INSTANCE;\n\n  @Override\n  public int compare(Symbol o1, Symbol o2) {\n    return o1.toString().compareTo(o2.toString());\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/symbols/SymbolManager.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.symbols;\n\nimport edu.harvard.seas.pl.formulog.ast.BindingType;\nimport edu.harvard.seas.pl.formulog.symbols.parameterized.BuiltInConstructorSymbolBase;\nimport edu.harvard.seas.pl.formulog.symbols.parameterized.Param;\nimport edu.harvard.seas.pl.formulog.symbols.parameterized.ParameterizedSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.parameterized.SymbolBase;\nimport edu.harvard.seas.pl.formulog.types.BuiltInTypes;\nimport edu.harvard.seas.pl.formulog.types.FunctorType;\nimport edu.harvard.seas.pl.formulog.types.Types.TypeIndex;\nimport edu.harvard.seas.pl.formulog.util.TodoException;\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\n\npublic class SymbolManager {\n\n  private final Map<String, Symbol> memo = new ConcurrentHashMap<>();\n  private final Set<TypeSymbol> typeSymbols = Util.concurrentSet();\n\n  public TypeSymbol createTypeSymbol(String name, int arity, TypeSymbolType st) {\n    checkNotUsed(name);\n    TypeSymbol sym = new TypeSymbolImpl(name, arity, st);\n    registerSymbol(sym);\n    return sym;\n  }\n\n  public ConstructorSymbol createConstructorSymbol(\n      String name, int arity, ConstructorSymbolType st, FunctorType type) {\n    checkNotUsed(name);\n    ConstructorSymbol sym = new ConstructorSymbolImpl(name, arity, st, type);\n    registerSymbol(sym);\n    return sym;\n  }\n\n  public RecordSymbol createRecordSymbol(\n      String name, int arity, FunctorType type, List<FunctionSymbol> labels) {\n    checkNotUsed(name);\n    RecordSymbol sym = new RecordSymbolImpl(name, arity, type, labels);\n    registerSymbol(sym);\n    return sym;\n  }\n\n  public FunctionSymbol createFunctionSymbol(String name, int arity, FunctorType type) {\n    checkNotUsed(name);\n    FunctionSymbol sym = new FunctionSymbolImpl(name, arity, type);\n    registerSymbol(sym);\n    return sym;\n  }\n\n  public MutableRelationSymbol createRelationSymbol(String name, int arity, FunctorType type) {\n    checkNotUsed(name);\n    MutableRelationSymbol sym = new RelationSymbolImpl(name, arity, type);\n    registerSymbol(sym);\n    return sym;\n  }\n\n  public void checkNotUsed(String name) {\n    if (hasName(name)) {\n      throw new IllegalArgumentException(\n          \"Cannot create symbol \" + name + \"; a symbol already exists with that name.\");\n    }\n  }\n\n  public boolean hasName(String name) {\n    return memo.containsKey(name) || GlobalSymbolManager.hasName(name);\n  }\n\n  public boolean hasConstructorSymbolWithName(String name) {\n    if (!hasName(name)) {\n      return false;\n    }\n    return lookupSymbol(name) instanceof ConstructorSymbol;\n  }\n\n  public Symbol lookupSymbol(String name) {\n    return lookupSymbol(name, Collections.emptyList());\n  }\n\n  public ParameterizedSymbol getParameterizedSymbol(SymbolBase base) {\n    if (base instanceof BuiltInConstructorSymbolBase) {\n      return GlobalSymbolManager.getParameterizedSymbol((BuiltInConstructorSymbolBase) base);\n    }\n    throw new TodoException();\n  }\n\n  public Symbol lookupSymbol(String name, List<Param> params) {\n    if (GlobalSymbolManager.hasName(name)) {\n      return GlobalSymbolManager.lookup(name, params);\n    }\n    if (!hasName(name)) {\n      throw new IllegalArgumentException(\"No symbol exists with name \" + name + \".\");\n    }\n    Symbol sym = memo.get(name);\n    assert sym != null;\n    if (sym instanceof ParameterizedSymbol) {\n      return ((ParameterizedSymbol) sym).copyWithNewArgs(params);\n    } else if (!params.isEmpty()) {\n      throw new IllegalArgumentException(\n          \"Cannot supply parameters to non-parameterized symbol: \" + sym);\n    }\n    return sym;\n  }\n\n  public PredicateFunctionSymbol createPredicateFunctionSymbol(\n      RelationSymbol sym, BindingType[] bindings) {\n    return PredicateFunctionSymbol.create(sym, bindings, this);\n  }\n\n  public PredicateFunctionSymbol createPredicateFunctionSymbolPlaceholder(RelationSymbol sym) {\n    return PredicateFunctionSymbol.createPlaceholder(sym, this);\n  }\n\n  public ConstructorSymbol lookupIndexConstructorSymbol(int index) {\n    String name = \"index$\" + index;\n    ConstructorSymbol sym = (ConstructorSymbol) memo.get(name);\n    if (sym == null) {\n      sym =\n          createConstructorSymbol(\n              name,\n              1,\n              ConstructorSymbolType.INDEX_CONSTRUCTOR,\n              new FunctorType(BuiltInTypes.i32, TypeIndex.make(index)));\n      registerSymbol(sym);\n    }\n    return sym;\n  }\n\n  private static class FunctionSymbolImpl extends AbstractTypedSymbol implements FunctionSymbol {\n\n    public FunctionSymbolImpl(String name, int arity, FunctorType type) {\n      super(name, arity, type);\n    }\n  }\n\n  private static class RecordSymbolImpl extends AbstractTypedSymbol implements RecordSymbol {\n\n    private final List<FunctionSymbol> labels;\n\n    public RecordSymbolImpl(String name, int arity, FunctorType type, List<FunctionSymbol> labels) {\n      super(name, arity, type);\n      this.labels = labels;\n    }\n\n    @Override\n    public ConstructorSymbolType getConstructorSymbolType() {\n      return ConstructorSymbolType.VANILLA_CONSTRUCTOR;\n    }\n\n    @Override\n    public List<FunctionSymbol> getLabels() {\n      return labels;\n    }\n  }\n\n  private static class RelationSymbolImpl extends AbstractTypedSymbol\n      implements MutableRelationSymbol {\n\n    private enum Mode {\n      vanillaIdb,\n      topDownIdb,\n      bottomUpIdb,\n      edb\n    }\n\n    private boolean disk;\n\n    private Mode mode = Mode.vanillaIdb;\n\n    public RelationSymbolImpl(String name, int arity, FunctorType type) {\n      super(name, arity, type);\n    }\n\n    @Override\n    public boolean isIdbSymbol() {\n      return !isEdbSymbol();\n    }\n\n    @Override\n    public boolean isEdbSymbol() {\n      return mode == Mode.edb;\n    }\n\n    @Override\n    public boolean isDisk() {\n      return disk;\n    }\n\n    @Override\n    public synchronized boolean isBottomUp() {\n      return mode == Mode.bottomUpIdb;\n    }\n\n    @Override\n    public synchronized boolean isTopDown() {\n      return mode == Mode.topDownIdb;\n    }\n\n    @Override\n    public synchronized void setTopDown() {\n      if (mode == Mode.bottomUpIdb) {\n        throw new IllegalStateException(\"Relation cannot be both top-down and bottom-up\");\n      }\n      mode = Mode.topDownIdb;\n    }\n\n    @Override\n    public synchronized void setBottomUp() {\n      if (mode == Mode.topDownIdb) {\n        throw new IllegalStateException(\"Relation cannot be both top-down and bottom-up\");\n      }\n      mode = Mode.bottomUpIdb;\n    }\n\n    @Override\n    public synchronized void setDisk() {\n      disk = true;\n    }\n\n    @Override\n    public void setEdb() {\n      if (mode != Mode.edb && mode != Mode.vanillaIdb) {\n        throw new IllegalStateException(\n            \"Relation cannot be an EDB relation with other qualifier: \" + mode);\n      }\n      mode = Mode.edb;\n    }\n  }\n\n  public void registerSymbol(Symbol sym) {\n    if (sym instanceof TypeSymbol) {\n      typeSymbols.add((TypeSymbol) sym);\n    }\n    Symbol sym2 = memo.putIfAbsent(sym.toString(), sym);\n    if (sym2 != null && !sym2.equals(sym)) {\n      throw new IllegalArgumentException(\n          \"Cannot register symbol \"\n              + sym\n              + \"; a different symbol is already registered with that name.\");\n    }\n  }\n\n  public Set<TypeSymbol> getTypeSymbols() {\n    Set<TypeSymbol> syms = new HashSet<>(typeSymbols);\n    syms.addAll(GlobalSymbolManager.getTypeSymbols());\n    return Collections.unmodifiableSet(syms);\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/symbols/TypeSymbol.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.symbols;\n\npublic interface TypeSymbol extends Symbol {\n\n  TypeSymbolType getTypeSymbolType();\n\n  default boolean isNormalType() {\n    return getTypeSymbolType().equals(TypeSymbolType.NORMAL_TYPE);\n  }\n\n  default boolean isAlias() {\n    return getTypeSymbolType().equals(TypeSymbolType.TYPE_ALIAS);\n  }\n\n  default boolean isUninterpretedSort() {\n    return getTypeSymbolType().equals(TypeSymbolType.UNINTERPRETED_SORT);\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/symbols/TypeSymbolImpl.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.symbols;\n\nclass TypeSymbolImpl extends AbstractSymbol implements TypeSymbol {\n\n  private final TypeSymbolType symType;\n\n  public TypeSymbolImpl(String name, int arity, TypeSymbolType symType) {\n    super(name, arity);\n    this.symType = symType;\n  }\n\n  @Override\n  public TypeSymbolType getTypeSymbolType() {\n    return symType;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/symbols/TypeSymbolType.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.symbols;\n\npublic enum TypeSymbolType {\n  NORMAL_TYPE,\n\n  TYPE_ALIAS,\n\n  UNINTERPRETED_SORT,\n  ;\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/symbols/TypedSymbol.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.symbols;\n\nimport edu.harvard.seas.pl.formulog.types.FunctorType;\n\npublic interface TypedSymbol extends Symbol {\n\n  FunctorType getCompileTimeType();\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/symbols/WrappedRelationSymbol.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.symbols;\n\npublic interface WrappedRelationSymbol<R extends RelationSymbol> extends RelationSymbol {\n\n  R getBaseSymbol();\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/symbols/parameterized/AbstractParameterizedSymbol.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.symbols.parameterized;\n\nimport edu.harvard.seas.pl.formulog.types.Types;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic abstract class AbstractParameterizedSymbol<B extends SymbolBase>\n    implements ParameterizedSymbol {\n\n  private final B base;\n  private final List<Param> args;\n\n  public AbstractParameterizedSymbol(B base, List<Param> args) {\n    if (base.getNumParams() != args.size()) {\n      throw new IllegalArgumentException(\n          \"Wrong number of parameters for symbol \"\n              + base\n              + \", which has parameter arity \"\n              + base.getNumParams()\n              + \" but received parameters \"\n              + args);\n    }\n    this.base = base;\n    this.args = new ArrayList<>();\n    List<ParamKind> kinds = base.getParamKinds();\n    for (int i = 0; i < kinds.size(); ++i) {\n      Param param = new Param(args.get(i).getType(), kinds.get(i));\n      this.args.add(param);\n    }\n  }\n\n  @Override\n  public int getArity() {\n    return base.getArity();\n  }\n\n  @Override\n  public String toString() {\n    String s = base.toString();\n    s += \"<\";\n    for (int i = 0; i < args.size(); ++i) {\n      s += args.get(i);\n      if (i < args.size() - 1) {\n        s += \", \";\n      }\n    }\n    return s + \">\";\n  }\n\n  @Override\n  public B getBase() {\n    return base;\n  }\n\n  @Override\n  public List<Param> getArgs() {\n    return args;\n  }\n\n  @Override\n  public boolean isGround() {\n    for (Param arg : args) {\n      if (Types.containsTypeVarOrOpaqueType(arg.getType())) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  @Override\n  public int hashCode() {\n    final int prime = 31;\n    int result = 1;\n    result = prime * result + ((args == null) ? 0 : args.hashCode());\n    result = prime * result + ((base == null) ? 0 : base.hashCode());\n    return result;\n  }\n\n  @Override\n  public boolean equals(Object obj) {\n    if (this == obj) return true;\n    if (obj == null) return false;\n    if (getClass() != obj.getClass()) return false;\n    @SuppressWarnings(\"rawtypes\")\n    AbstractParameterizedSymbol other = (AbstractParameterizedSymbol) obj;\n    if (args == null) {\n      if (other.args != null) return false;\n    } else if (!args.equals(other.args)) return false;\n    if (base == null) {\n      if (other.base != null) return false;\n    } else if (!base.equals(other.base)) return false;\n    return true;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/symbols/parameterized/BuiltInConstructorSymbolBase.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.symbols.parameterized;\n\nimport edu.harvard.seas.pl.formulog.symbols.ConstructorSymbolType;\nimport java.util.Arrays;\nimport java.util.List;\n\npublic enum BuiltInConstructorSymbolBase implements FunctorBase {\n\n  // Bit vectors\n\n  BV_SLT(\"bv_slt\", 2, ParamKind.NAT),\n\n  BV_SLE(\"bv_sle\", 2, ParamKind.NAT),\n\n  BV_SGT(\"bv_sgt\", 2, ParamKind.NAT),\n\n  BV_SGE(\"bv_sge\", 2, ParamKind.NAT),\n\n  BV_ULT(\"bv_ult\", 2, ParamKind.NAT),\n\n  BV_ULE(\"bv_ule\", 2, ParamKind.NAT),\n\n  BV_UGT(\"bv_ugt\", 2, ParamKind.NAT),\n\n  BV_UGE(\"bv_uge\", 2, ParamKind.NAT),\n\n  BV_CONST(\"bv_const\", 1, ParamKind.NAT),\n\n  BV_BIG_CONST(\"bv_big_const\", 1, ParamKind.NAT),\n\n  BV_EXTRACT(\"bv_extract\", 3, ParamKind.NAT, ParamKind.NAT),\n\n  BV_CONCAT(\"bv_concat\", 2, ParamKind.NAT, ParamKind.NAT, ParamKind.NAT),\n\n  BV_TO_BV_SIGNED(\"bv_to_bv_signed\", 1, ParamKind.NAT, ParamKind.NAT),\n\n  BV_TO_BV_UNSIGNED(\"bv_to_bv_unsigned\", 1, ParamKind.NAT, ParamKind.NAT),\n\n  FP_TO_SBV(\"fp_to_sbv\", 1, ParamKind.NAT, ParamKind.NAT, ParamKind.NAT),\n\n  FP_TO_UBV(\"fp_to_ubv\", 1, ParamKind.NAT, ParamKind.NAT, ParamKind.NAT),\n\n  INT_TO_BV(\"int_to_bv\", 1, ParamKind.NAT),\n\n  BV_TO_INT(\"bv_to_int\", 1, ParamKind.NAT),\n\n  // Floating point\n\n  FP_EQ(\"fp_eq\", 2, ParamKind.NAT, ParamKind.NAT),\n\n  FP_LT(\"fp_lt\", 2, ParamKind.NAT, ParamKind.NAT),\n\n  FP_LE(\"fp_le\", 2, ParamKind.NAT, ParamKind.NAT),\n\n  FP_GT(\"fp_gt\", 2, ParamKind.NAT, ParamKind.NAT),\n\n  FP_GE(\"fp_ge\", 2, ParamKind.NAT, ParamKind.NAT),\n\n  FP_IS_NAN(\"fp_is_nan\", 1, ParamKind.NAT, ParamKind.NAT),\n\n  FP_CONST(\"fp_const\", 1, ParamKind.NAT, ParamKind.NAT),\n\n  FP_BIG_CONST(\"fp_big_const\", 1, ParamKind.NAT, ParamKind.NAT),\n\n  FP_TO_FP(\"fp_to_fp\", 1, ParamKind.NAT, ParamKind.NAT, ParamKind.NAT, ParamKind.NAT),\n\n  BV_TO_FP(\"bv_to_fp\", 1, ParamKind.NAT, ParamKind.NAT, ParamKind.NAT),\n\n  // Logical connectives\n\n  SMT_PAT(\"smt_pat\", 1, ParamKind.SMT_REPRESENTABLE_TYPE),\n\n  SMT_WRAP_VAR(\"smt_wrap_var\", 1, ParamKind.SMT_REPRESENTABLE_TYPE),\n\n  SMT_EQ(\"smt_eq\", 2, ParamKind.PRE_SMT_TYPE),\n\n  SMT_LET(\"smt_let\", 3, ParamKind.PRE_SMT_TYPE),\n\n  // Arrays\n\n  ARRAY_SELECT(\"array_select\", 2, ParamKind.PRE_SMT_TYPE),\n\n  ARRAY_DEFAULT(\"array_default\", 1, ParamKind.PRE_SMT_TYPE),\n\n  // Solver variables\n\n  SMT_VAR(\"smt_var\", 1, ParamKind.ANY_TYPE, ParamKind.PRE_SMT_TYPE),\n  ;\n\n  private final String name;\n  private final int arity;\n  private final List<ParamKind> paramTypes;\n\n  private BuiltInConstructorSymbolBase(String name, int arity, ParamKind... paramTypes) {\n    this.name = name;\n    this.arity = arity;\n    this.paramTypes = Arrays.asList(paramTypes);\n  }\n\n  @Override\n  public int getArity() {\n    return arity;\n  }\n\n  @Override\n  public String toString() {\n    return name;\n  }\n\n  @Override\n  public List<ParamKind> getParamKinds() {\n    return paramTypes;\n  }\n\n  public ConstructorSymbolType getConstructorSymbolType() {\n    return ConstructorSymbolType.SOLVER_EXPR;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/symbols/parameterized/FunctorBase.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.symbols.parameterized;\n\npublic interface FunctorBase extends SymbolBase {}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/symbols/parameterized/Param.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.symbols.parameterized;\n\nimport edu.harvard.seas.pl.formulog.types.Types;\nimport edu.harvard.seas.pl.formulog.types.Types.AlgebraicDataType;\nimport edu.harvard.seas.pl.formulog.types.Types.Type;\nimport edu.harvard.seas.pl.formulog.types.Types.TypeIndex;\nimport edu.harvard.seas.pl.formulog.types.Types.TypeVar;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\npublic class Param {\n\n  private final Type type;\n  private final ParamKind kind;\n\n  public Param(Type type, ParamKind kind) {\n    this.type = type;\n    this.kind = kind;\n    if (!check()) {\n      throw new IllegalArgumentException(\n          \"Cannot instantiate parameter of kind \" + kind + \" with type \" + type);\n    }\n  }\n\n  private boolean check() {\n    if (type.isVar()) {\n      return true;\n    }\n    switch (kind) {\n      case ANY_TYPE:\n        return !type.isIndex();\n      case WILD_CARD:\n        return true;\n      case SMT_REPRESENTABLE_TYPE:\n        return Types.isSmtRepresentable(type);\n      case NAT:\n        return type.isIndex();\n      case PRE_SMT_TYPE:\n        return Types.mayBePreSmtType(type);\n      case SMT_VAR:\n        return Types.isSmtVarType(type);\n      case SMT_VARS:\n        if (Types.isSmtVarType(type)) {\n          return true;\n        }\n        if (!Types.isTupleType(type)) {\n          return false;\n        }\n        for (Type typeArg : ((AlgebraicDataType) type).getTypeArgs()) {\n          if (!typeArg.isVar() && !Types.isSmtVarType(type)) {\n            return false;\n          }\n        }\n        return true;\n    }\n    throw new AssertionError(\"impossible\");\n  }\n\n  public Type getType() {\n    return type;\n  }\n\n  public ParamKind getKind() {\n    return kind;\n  }\n\n  boolean isGround() {\n    return !Types.containsTypeVarOrOpaqueType(getType());\n  }\n\n  public static List<Param> wildCards(int howMany) {\n    List<Param> params = new ArrayList<>();\n    for (int i = 0; i < howMany; ++i) {\n      params.add(wildCard());\n    }\n    return params;\n  }\n\n  public static List<Param> applySubst(Iterable<Param> params, Map<TypeVar, Type> subst) {\n    List<Param> newParams = new ArrayList<>();\n    for (Param param : params) {\n      newParams.add(new Param(param.getType().applySubst(subst), param.getKind()));\n    }\n    return newParams;\n  }\n\n  public static Param nat(int index) {\n    return new Param(TypeIndex.make(index), ParamKind.NAT);\n  }\n\n  public static Param nat(Type type) {\n    return new Param(type, ParamKind.NAT);\n  }\n\n  public static Param wildCard() {\n    return new Param(TypeVar.fresh(), ParamKind.WILD_CARD);\n  }\n\n  public static Param wildCard(Type type) {\n    return new Param(type, ParamKind.WILD_CARD);\n  }\n\n  @Override\n  public int hashCode() {\n    final int prime = 31;\n    int result = 1;\n    result = prime * result + ((kind == null) ? 0 : kind.hashCode());\n    result = prime * result + ((type == null) ? 0 : type.hashCode());\n    return result;\n  }\n\n  @Override\n  public boolean equals(Object obj) {\n    if (this == obj) return true;\n    if (obj == null) return false;\n    if (getClass() != obj.getClass()) return false;\n    Param other = (Param) obj;\n    if (kind != other.kind) return false;\n    if (type == null) {\n      if (other.type != null) return false;\n    } else if (!type.equals(other.type)) return false;\n    return true;\n  }\n\n  @Override\n  public String toString() {\n    return type + \":\" + kind;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/symbols/parameterized/ParamKind.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.symbols.parameterized;\n\npublic enum ParamKind {\n  NAT,\n\n  ANY_TYPE,\n\n  SMT_REPRESENTABLE_TYPE,\n\n  SMT_VAR,\n\n  SMT_VARS,\n\n  PRE_SMT_TYPE,\n\n  WILD_CARD;\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/symbols/parameterized/ParameterizedConstructorSymbol.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.symbols.parameterized;\n\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.array;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.bool;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.bv;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.fp;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.fp32;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.fp64;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.i32;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.i64;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.int_;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.smt;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.smtPattern;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.smtWrappedVar;\nimport static edu.harvard.seas.pl.formulog.types.BuiltInTypes.sym;\n\nimport edu.harvard.seas.pl.formulog.symbols.ConstructorSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.ConstructorSymbolType;\nimport edu.harvard.seas.pl.formulog.types.FunctorType;\nimport edu.harvard.seas.pl.formulog.types.Types.Type;\nimport edu.harvard.seas.pl.formulog.types.Types.TypeIndex;\nimport edu.harvard.seas.pl.formulog.types.Types.TypeVar;\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\npublic class ParameterizedConstructorSymbol\n    extends AbstractParameterizedSymbol<BuiltInConstructorSymbolBase> implements ConstructorSymbol {\n\n  private final FunctorType type;\n  private static final Map<\n          Pair<BuiltInConstructorSymbolBase, List<Param>>, ParameterizedConstructorSymbol>\n      memo = new ConcurrentHashMap<>();\n\n  private ParameterizedConstructorSymbol(BuiltInConstructorSymbolBase base, List<Param> args) {\n    super(base, args);\n    this.type = makeType();\n  }\n\n  public static ParameterizedConstructorSymbol mk(\n      BuiltInConstructorSymbolBase base, List<Param> args) {\n    switch (base) {\n      case ARRAY_DEFAULT:\n      case ARRAY_SELECT:\n      case BV_BIG_CONST:\n      case BV_CONST:\n      case BV_SGE:\n      case BV_SGT:\n      case BV_SLE:\n      case BV_SLT:\n      case BV_TO_BV_SIGNED:\n      case BV_TO_BV_UNSIGNED:\n      case BV_UGE:\n      case BV_UGT:\n      case BV_ULE:\n      case BV_ULT:\n      case BV_CONCAT:\n      case BV_EXTRACT:\n      case SMT_EQ:\n      case SMT_LET:\n      case SMT_PAT:\n      case SMT_WRAP_VAR:\n      case SMT_VAR:\n      case BV_TO_INT:\n      case INT_TO_BV:\n        break;\n      case FP_BIG_CONST:\n      case FP_CONST:\n      case FP_EQ:\n      case FP_GE:\n      case FP_GT:\n      case FP_IS_NAN:\n      case FP_LE:\n      case FP_LT:\n        if (args.size() == 1) {\n          args = new ArrayList<>(expandAsFpAlias(args.get(0)));\n        }\n        break;\n      case FP_TO_SBV:\n      case FP_TO_UBV:\n        if (args.size() == 2) {\n          Param bv = args.get(1);\n          args = new ArrayList<>(expandAsFpAlias(args.get(0)));\n          args.add(bv);\n        }\n        break;\n      case FP_TO_FP:\n        if (args.size() == 2) {\n          Param fp1 = args.get(0);\n          Param fp2 = args.get(1);\n          args = new ArrayList<>(expandAsFpAlias(fp1));\n          args.addAll(expandAsFpAlias(fp2));\n        }\n        break;\n      case BV_TO_FP:\n        if (args.size() == 2) {\n          List<Param> newArgs = new ArrayList<>();\n          newArgs.add(args.get(0));\n          newArgs.addAll(expandAsFpAlias(args.get(1)));\n          args = newArgs;\n        }\n        break;\n      default:\n        throw new AssertionError(\"Unexpected symbol: \" + base);\n    }\n    if (args.isEmpty()) {\n      args = Param.wildCards(base.getNumParams());\n    }\n    List<Param> args2 = List.copyOf(args);\n    return new ParameterizedConstructorSymbol(base, args2);\n  }\n\n  private static List<Param> expandAsFpAlias(Param param) {\n    if (!param.getKind().equals(ParamKind.NAT) || !param.isGround()) {\n      return Collections.singletonList(param);\n    }\n    TypeIndex nat = (TypeIndex) param.getType();\n    List<TypeIndex> indices = nat.expandAsFpIndex();\n    List<Param> params = new ArrayList<>();\n    for (TypeIndex index : indices) {\n      params.add(Param.nat(index.getIndex()));\n    }\n    return params;\n  }\n\n  @Override\n  public ParameterizedConstructorSymbol copyWithNewArgs(List<Param> args) {\n    return mk(getBase(), args);\n  }\n\n  @Override\n  public ParameterizedConstructorSymbol copyWithNewArgs(Param... args) {\n    return copyWithNewArgs(Arrays.asList(args));\n  }\n\n  public ConstructorSymbolType getConstructorSymbolType() {\n    return ConstructorSymbolType.SOLVER_EXPR;\n  }\n\n  private FunctorType makeType() {\n    List<Type> types = new ArrayList<>();\n    for (Param param : getArgs()) {\n      types.add(param.getType());\n    }\n    switch (getBase()) {\n      case ARRAY_DEFAULT:\n        {\n          Type a = types.get(0);\n          Type b = TypeVar.fresh();\n          return mkType(smt(array(a, b)), smt(b));\n        }\n      case ARRAY_SELECT:\n        {\n          Type a = types.get(0);\n          Type b = TypeVar.fresh();\n          return mkType(smt(array(a, b)), smt(a), smt(b));\n        }\n      case BV_BIG_CONST:\n        {\n          Type width = types.get(0);\n          return mkType(i64, smt(bv(width)));\n        }\n      case BV_CONST:\n        {\n          Type width = types.get(0);\n          return mkType(i32, smt(bv(width)));\n        }\n      case BV_CONCAT:\n        {\n          Type w1 = types.get(0);\n          Type w2 = types.get(1);\n          Type w3 = types.get(2);\n          return mkType(smt(bv(w1)), smt(bv(w2)), smt(bv(w3)));\n        }\n      case BV_EXTRACT:\n        {\n          Type w1 = types.get(0);\n          Type w2 = types.get(1);\n          return mkType(smt(bv(w1)), i32, i32, smt(bv(w2)));\n        }\n      case INT_TO_BV:\n        return mkType(smt(int_), smt(bv(types.get(0))));\n      case BV_TO_INT:\n        return mkType(smt(bv(types.get(0))), smt(int_));\n      case BV_SGE:\n      case BV_SGT:\n      case BV_SLE:\n      case BV_SLT:\n      case BV_UGE:\n      case BV_UGT:\n      case BV_ULE:\n      case BV_ULT:\n        {\n          Type width = types.get(0);\n          return mkType(smt(bv(width)), smt(bv(width)), smt(bool));\n        }\n      case BV_TO_BV_SIGNED:\n      case BV_TO_BV_UNSIGNED:\n        {\n          Type fromWidth = types.get(0);\n          Type toWidth = types.get(1);\n          return mkType(smt(bv(fromWidth)), smt(bv(toWidth)));\n        }\n      case BV_TO_FP:\n        {\n          Type width = types.get(0);\n          Type exponent = types.get(1);\n          Type significand = types.get(2);\n          return mkType(smt(bv(width)), smt(fp(exponent, significand)));\n        }\n      case FP_BIG_CONST:\n        {\n          Type exponent = types.get(0);\n          Type significand = types.get(1);\n          return mkType(fp64, smt(fp(exponent, significand)));\n        }\n      case FP_CONST:\n        {\n          Type exponent = types.get(0);\n          Type significand = types.get(1);\n          return mkType(fp32, smt(fp(exponent, significand)));\n        }\n      case FP_EQ:\n      case FP_GE:\n      case FP_GT:\n      case FP_LE:\n      case FP_LT:\n        {\n          Type exponent = types.get(0);\n          Type significand = types.get(1);\n          Type fp = fp(exponent, significand);\n          return mkType(smt(fp), smt(fp), smt(bool));\n        }\n      case FP_IS_NAN:\n        {\n          Type exponent = types.get(0);\n          Type significand = types.get(1);\n          return mkType(smt(fp(exponent, significand)), smt(bool));\n        }\n      case FP_TO_SBV:\n      case FP_TO_UBV:\n        {\n          Type exponent = types.get(0);\n          Type significand = types.get(1);\n          Type width = types.get(2);\n          return mkType(smt(fp(exponent, significand)), smt(bv(width)));\n        }\n      case FP_TO_FP:\n        {\n          Type exp1 = types.get(0);\n          Type sig1 = types.get(1);\n          Type exp2 = types.get(2);\n          Type sig2 = types.get(3);\n          return mkType(smt(fp(exp1, sig1)), smt(fp(exp2, sig2)));\n        }\n      case SMT_EQ:\n        {\n          Type ty = types.get(0);\n          return mkType(smt(ty), smt(ty), smt(bool));\n        }\n      case SMT_LET:\n        {\n          Type a = types.get(0);\n          Type b = TypeVar.fresh();\n          return mkType(sym(a), smt(a), smt(b), smt(b));\n        }\n      case SMT_PAT:\n        {\n          return mkType(types.get(0), smtPattern);\n        }\n      case SMT_WRAP_VAR:\n        {\n          return mkType(sym(types.get(0)), smtWrappedVar);\n        }\n      case SMT_VAR:\n        {\n          return mkType(types.get(0), sym(types.get(1)));\n        }\n    }\n    throw new AssertionError(\"impossible\");\n  }\n\n  @Override\n  public FunctorType getCompileTimeType() {\n    return type;\n  }\n\n  private static FunctorType mkType(Type... types) {\n    return new FunctorType(types);\n  }\n\n  @Override\n  public ParameterizedSymbol makeFinal() {\n    return Util.lookupOrCreate(\n        memo,\n        new Pair<>(getBase(), getArgs()),\n        () -> new ParameterizedConstructorSymbol(getBase(), getArgs()));\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/symbols/parameterized/ParameterizedSymbol.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.symbols.parameterized;\n\nimport edu.harvard.seas.pl.formulog.symbols.Symbol;\nimport java.util.List;\n\npublic interface ParameterizedSymbol extends Symbol {\n\n  SymbolBase getBase();\n\n  List<Param> getArgs();\n\n  ParameterizedSymbol copyWithNewArgs(List<Param> args);\n\n  ParameterizedSymbol copyWithNewArgs(Param... args);\n\n  ParameterizedSymbol makeFinal();\n\n  boolean isGround();\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/symbols/parameterized/SymbolBase.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.symbols.parameterized;\n\nimport java.util.List;\n\npublic interface SymbolBase {\n\n  int getArity();\n\n  List<ParamKind> getParamKinds();\n\n  default int getNumParams() {\n    return getParamKinds().size();\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/types/BuiltInTypes.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.types;\n\nimport static edu.harvard.seas.pl.formulog.symbols.BuiltInConstructorSymbol.CMP_EQ;\nimport static edu.harvard.seas.pl.formulog.symbols.BuiltInConstructorSymbol.CMP_GT;\nimport static edu.harvard.seas.pl.formulog.symbols.BuiltInConstructorSymbol.CMP_LT;\nimport static edu.harvard.seas.pl.formulog.symbols.BuiltInConstructorSymbol.CONS;\nimport static edu.harvard.seas.pl.formulog.symbols.BuiltInConstructorSymbol.NIL;\nimport static edu.harvard.seas.pl.formulog.symbols.BuiltInConstructorSymbol.NONE;\nimport static edu.harvard.seas.pl.formulog.symbols.BuiltInConstructorSymbol.SOME;\nimport static edu.harvard.seas.pl.formulog.symbols.BuiltInTypeSymbol.ARRAY_TYPE;\nimport static edu.harvard.seas.pl.formulog.symbols.BuiltInTypeSymbol.BOOL_TYPE;\nimport static edu.harvard.seas.pl.formulog.symbols.BuiltInTypeSymbol.BV;\nimport static edu.harvard.seas.pl.formulog.symbols.BuiltInTypeSymbol.CMP_TYPE;\nimport static edu.harvard.seas.pl.formulog.symbols.BuiltInTypeSymbol.FP;\nimport static edu.harvard.seas.pl.formulog.symbols.BuiltInTypeSymbol.INT_TYPE;\nimport static edu.harvard.seas.pl.formulog.symbols.BuiltInTypeSymbol.LIST_TYPE;\nimport static edu.harvard.seas.pl.formulog.symbols.BuiltInTypeSymbol.MODEL_TYPE;\nimport static edu.harvard.seas.pl.formulog.symbols.BuiltInTypeSymbol.OPAQUE_SET;\nimport static edu.harvard.seas.pl.formulog.symbols.BuiltInTypeSymbol.OPTION_TYPE;\nimport static edu.harvard.seas.pl.formulog.symbols.BuiltInTypeSymbol.SMT_PATTERN_TYPE;\nimport static edu.harvard.seas.pl.formulog.symbols.BuiltInTypeSymbol.SMT_TYPE;\nimport static edu.harvard.seas.pl.formulog.symbols.BuiltInTypeSymbol.SMT_WRAPPED_VAR_TYPE;\nimport static edu.harvard.seas.pl.formulog.symbols.BuiltInTypeSymbol.STRING_TYPE;\nimport static edu.harvard.seas.pl.formulog.symbols.BuiltInTypeSymbol.SYM_TYPE;\n\nimport edu.harvard.seas.pl.formulog.symbols.BuiltInConstructorGetterSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.ConstructorSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.GlobalSymbolManager;\nimport edu.harvard.seas.pl.formulog.types.Types.AlgebraicDataType;\nimport edu.harvard.seas.pl.formulog.types.Types.AlgebraicDataType.ConstructorScheme;\nimport edu.harvard.seas.pl.formulog.types.Types.Type;\nimport edu.harvard.seas.pl.formulog.types.Types.TypeIndex;\nimport edu.harvard.seas.pl.formulog.types.Types.TypeVar;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\npublic final class BuiltInTypes {\n\n  private BuiltInTypes() {\n    throw new AssertionError();\n  }\n\n  public static final TypeVar a = TypeVar.fresh();\n  public static final TypeVar b = TypeVar.fresh();\n  public static final TypeVar c = TypeVar.fresh();\n  public static final TypeVar d = TypeVar.fresh();\n\n  public static final Type i32 = bv(32);\n  public static final Type i64 = bv(64);\n  public static final Type fp32 = fp(8, 24);\n  public static final Type fp64 = fp(11, 53);\n  public static final Type string = AlgebraicDataType.make(STRING_TYPE);\n  public static final Type bool = AlgebraicDataType.make(BOOL_TYPE);\n  public static final Type cmp = AlgebraicDataType.make(CMP_TYPE);\n  public static final Type model = AlgebraicDataType.make(MODEL_TYPE);\n  public static final Type int_ = AlgebraicDataType.make(INT_TYPE);\n  public static final Type smtPattern = AlgebraicDataType.make(SMT_PATTERN_TYPE);\n  public static final Type smtWrappedVar = AlgebraicDataType.make(SMT_WRAPPED_VAR_TYPE);\n\n  static {\n    // Only need to set constructors for types that should be interpreted as\n    // algebraic data types in SMT-LIB.\n    setCmpConstructors();\n    setListConstructors();\n    setOptionConstructors();\n  }\n\n  private static void setCmpConstructors() {\n    ConstructorScheme gt =\n        new ConstructorScheme(CMP_GT, Collections.emptyList(), Collections.emptyList());\n    ConstructorScheme lt =\n        new ConstructorScheme(CMP_LT, Collections.emptyList(), Collections.emptyList());\n    ConstructorScheme eq =\n        new ConstructorScheme(CMP_EQ, Collections.emptyList(), Collections.emptyList());\n    AlgebraicDataType.setConstructors(CMP_TYPE, Collections.emptyList(), Arrays.asList(gt, lt, eq));\n  }\n\n  private static void setListConstructors() {\n    ConstructorScheme nil =\n        new ConstructorScheme(NIL, Collections.emptyList(), Collections.emptyList());\n    List<ConstructorSymbol> consGetters =\n        Arrays.asList(BuiltInConstructorGetterSymbol.CONS_1, BuiltInConstructorGetterSymbol.CONS_2);\n    ConstructorScheme cons = new ConstructorScheme(CONS, Arrays.asList(a, list(a)), consGetters);\n    AlgebraicDataType.setConstructors(\n        LIST_TYPE, Collections.singletonList(a), Arrays.asList(nil, cons));\n  }\n\n  private static void setOptionConstructors() {\n    ConstructorScheme none =\n        new ConstructorScheme(NONE, Collections.emptyList(), Collections.emptyList());\n    List<ConstructorSymbol> someGetters = Arrays.asList(BuiltInConstructorGetterSymbol.SOME_1);\n    ConstructorScheme some = new ConstructorScheme(SOME, Collections.singletonList(a), someGetters);\n    AlgebraicDataType.setConstructors(\n        OPTION_TYPE, Collections.singletonList(a), Arrays.asList(none, some));\n  }\n\n  public static AlgebraicDataType list(Type a) {\n    return AlgebraicDataType.make(LIST_TYPE, Collections.singletonList(a));\n  }\n\n  public static AlgebraicDataType option(Type a) {\n    return AlgebraicDataType.make(OPTION_TYPE, Collections.singletonList(a));\n  }\n\n  public static AlgebraicDataType smt(Type a) {\n    return AlgebraicDataType.make(SMT_TYPE, Collections.singletonList(a));\n  }\n\n  public static AlgebraicDataType sym(Type a) {\n    return AlgebraicDataType.make(SYM_TYPE, Collections.singletonList(a));\n  }\n\n  public static AlgebraicDataType bv(Type a) {\n    return AlgebraicDataType.make(BV, a);\n  }\n\n  public static AlgebraicDataType bv(int width) {\n    return bv(TypeIndex.make(width));\n  }\n\n  public static AlgebraicDataType fp(Type a, Type b) {\n    return AlgebraicDataType.make(FP, a, b);\n  }\n\n  public static AlgebraicDataType fp(int exponent, int significand) {\n    return fp(TypeIndex.make(exponent), TypeIndex.make(significand));\n  }\n\n  public static AlgebraicDataType array(Type a, Type b) {\n    return AlgebraicDataType.make(ARRAY_TYPE, Arrays.asList(a, b));\n  }\n\n  public static AlgebraicDataType opaqueSet(Type a) {\n    return AlgebraicDataType.make(OPAQUE_SET, a);\n  }\n\n  public static AlgebraicDataType pair(Type a, Type b) {\n    return AlgebraicDataType.make(GlobalSymbolManager.lookupTupleTypeSymbol(2), a, b);\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/types/FunctorType.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.types;\n\nimport edu.harvard.seas.pl.formulog.types.Types.AlgebraicDataType;\nimport edu.harvard.seas.pl.formulog.types.Types.OpaqueType;\nimport edu.harvard.seas.pl.formulog.types.Types.Type;\nimport edu.harvard.seas.pl.formulog.types.Types.TypeIndex;\nimport edu.harvard.seas.pl.formulog.types.Types.TypeVar;\nimport edu.harvard.seas.pl.formulog.types.Types.TypeVisitor;\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\n\npublic class FunctorType implements Type {\n\n  private final List<Type> argTypes;\n  private final Type retType;\n\n  public FunctorType(List<Type> argTypes, Type retType) {\n    this.argTypes = argTypes;\n    this.retType = retType;\n  }\n\n  public FunctorType(Type... types) {\n    assert types.length > 0;\n    argTypes = new ArrayList<>(Arrays.asList(types));\n    retType = this.argTypes.remove(types.length - 1);\n  }\n\n  public List<Type> getArgTypes() {\n    return argTypes;\n  }\n\n  public Type getRetType() {\n    return retType;\n  }\n\n  public FunctorType freshen() {\n    Map<TypeVar, TypeVar> subst = new HashMap<>();\n    TypeVisitor<Void, Type> visitor =\n        new TypeVisitor<Void, Type>() {\n\n          @Override\n          public Type visit(TypeVar typeVar, Void in) {\n            return Util.lookupOrCreate(subst, typeVar, () -> TypeVar.fresh());\n          }\n\n          @Override\n          public Type visit(OpaqueType opaqueType, Void in) {\n            throw new AssertionError();\n          }\n\n          private List<Type> processTypeList(List<Type> types) {\n            List<Type> newTypes = new ArrayList<>();\n            for (Type t : types) {\n              newTypes.add(t.accept(this, null));\n            }\n            return newTypes;\n          }\n\n          @Override\n          public Type visit(AlgebraicDataType namedType, Void in) {\n            return AlgebraicDataType.make(\n                namedType.getSymbol(), processTypeList(namedType.getTypeArgs()));\n          }\n\n          @Override\n          public Type visit(TypeIndex typeIndex, Void in) {\n            return typeIndex;\n          }\n        };\n    List<Type> newArgTypes = new ArrayList<>();\n    for (Type t : argTypes) {\n      newArgTypes.add(t.accept(visitor, null));\n    }\n    Type newRetType = retType.accept(visitor, null);\n    return new FunctorType(newArgTypes, newRetType);\n  }\n\n  @Override\n  public int hashCode() {\n    final int prime = 31;\n    int result = 1;\n    result = prime * result + ((argTypes == null) ? 0 : argTypes.hashCode());\n    result = prime * result + ((retType == null) ? 0 : retType.hashCode());\n    return result;\n  }\n\n  @Override\n  public boolean equals(Object obj) {\n    if (this == obj) return true;\n    if (obj == null) return false;\n    if (getClass() != obj.getClass()) return false;\n    FunctorType other = (FunctorType) obj;\n    if (argTypes == null) {\n      if (other.argTypes != null) return false;\n    } else if (!argTypes.equals(other.argTypes)) return false;\n    if (retType == null) {\n      if (other.retType != null) return false;\n    } else if (!retType.equals(other.retType)) return false;\n    return true;\n  }\n\n  @Override\n  public <I, O> O accept(TypeVisitor<I, O> visitor, I in) {\n    throw new UnsupportedOperationException();\n  }\n\n  @Override\n  public Type applySubst(Map<TypeVar, ? extends Type> subst) {\n    List<Type> newArgTypes = Util.map(argTypes, t -> t.applySubst(subst));\n    Type newRetType = retType.applySubst(subst);\n    return new FunctorType(newArgTypes, newRetType);\n  }\n\n  @Override\n  public String toString() {\n    String s = \"(\";\n    for (Iterator<Type> it = argTypes.iterator(); it.hasNext(); ) {\n      s += it.next();\n      if (it.hasNext()) {\n        s += \", \";\n      }\n    }\n    s += \") -> \" + retType;\n    return s;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/types/IndexedType.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.types;\n\nimport edu.harvard.seas.pl.formulog.types.Types.Type;\nimport java.util.List;\n\npublic interface IndexedType {\n\n  Type instantiate(List<Integer> indices);\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/types/TypeAlias.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.types;\n\nimport edu.harvard.seas.pl.formulog.symbols.TypeSymbol;\nimport edu.harvard.seas.pl.formulog.types.Types.Type;\nimport edu.harvard.seas.pl.formulog.types.Types.TypeVar;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\n\npublic class TypeAlias {\n\n  private final TypeSymbol sym;\n  private final List<TypeVar> params;\n  private final Type type;\n\n  public TypeAlias(TypeSymbol sym, List<TypeVar> params, Type type) {\n    this.sym = sym;\n    this.params = params;\n    this.type = type;\n    if (!sym.isAlias()) {\n      throw new IllegalArgumentException(\"Cannot create an alias for non-type alias symbol \" + sym);\n    }\n    if (this.sym.getArity() != this.params.size()) {\n      throw new IllegalArgumentException(\n          \"Mismatch between symbol arity and number of type parameters: \" + this.sym);\n    }\n    if (!Types.getTypeVars(this.type).containsAll(this.params)) {\n      throw new IllegalArgumentException(\n          \"Unbound type variable in definition of type: \" + this.sym);\n    }\n  }\n\n  public static TypeAlias get(TypeSymbol sym, List<TypeVar> params, Type type) {\n    return new TypeAlias(sym, params, type);\n  }\n\n  public TypeSymbol getSymbol() {\n    return sym;\n  }\n\n  public int getNumberOfParams() {\n    return params.size();\n  }\n\n  public Type getParam(int idx) {\n    if (idx < 0 || idx >= params.size()) {\n      throw new IllegalArgumentException(\"Out of bounds parameter: \" + idx);\n    }\n    return params.get(idx);\n  }\n\n  @Override\n  public String toString() {\n    if (params.isEmpty()) {\n      return sym.toString() + \" = \" + type;\n    }\n    if (params.size() == 1) {\n      return params.get(0) + \" \" + sym + \" = \" + type;\n    }\n    StringBuilder sb = new StringBuilder('(');\n    Iterator<TypeVar> it = params.iterator();\n    sb.append(it.next());\n    while (it.hasNext()) {\n      sb.append(\", \" + it.next());\n    }\n    sb.append(\") \" + sym + \" = \" + type);\n    return sb.toString();\n  }\n\n  @Override\n  public int hashCode() {\n    final int prime = 31;\n    int result = 1;\n    result = prime * result + ((params == null) ? 0 : params.hashCode());\n    result = prime * result + ((sym == null) ? 0 : sym.hashCode());\n    result = prime * result + ((type == null) ? 0 : type.hashCode());\n    return result;\n  }\n\n  @Override\n  public boolean equals(Object obj) {\n    if (this == obj) return true;\n    if (obj == null) return false;\n    if (getClass() != obj.getClass()) return false;\n    TypeAlias other = (TypeAlias) obj;\n    if (params == null) {\n      if (other.params != null) return false;\n    } else if (!params.equals(other.params)) return false;\n    if (sym == null) {\n      if (other.sym != null) return false;\n    } else if (!sym.equals(other.sym)) return false;\n    if (type == null) {\n      if (other.type != null) return false;\n    } else if (!type.equals(other.type)) return false;\n    return true;\n  }\n\n  public List<TypeVar> getParams() {\n    return params;\n  }\n\n  public Type instantiate(List<Type> paramTypes) {\n    if (paramTypes.size() != params.size()) {\n      throw new IllegalArgumentException();\n    }\n    Map<TypeVar, Type> subst = new HashMap<>();\n    for (int i = 0; i < params.size(); ++i) {\n      TypeChecker.addBinding(params.get(i), paramTypes.get(i), subst);\n    }\n    return type.applySubst(subst);\n  }\n\n  public Type getType() {\n    return type;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/types/TypeChecker.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.types;\n\nimport edu.harvard.seas.pl.formulog.Main;\nimport edu.harvard.seas.pl.formulog.ast.*;\nimport edu.harvard.seas.pl.formulog.ast.ComplexLiterals.ComplexLiteralExnVisitor;\nimport edu.harvard.seas.pl.formulog.ast.ComplexLiterals.ComplexLiteralVisitor;\nimport edu.harvard.seas.pl.formulog.ast.Exprs.ExprVisitor;\nimport edu.harvard.seas.pl.formulog.ast.Exprs.ExprVisitorExn;\nimport edu.harvard.seas.pl.formulog.ast.FunctionCallFactory.FunctionCall;\nimport edu.harvard.seas.pl.formulog.ast.Terms.TermVisitor;\nimport edu.harvard.seas.pl.formulog.ast.Terms.TermVisitorExn;\nimport edu.harvard.seas.pl.formulog.functions.FunctionDef;\nimport edu.harvard.seas.pl.formulog.functions.FunctionDefManager;\nimport edu.harvard.seas.pl.formulog.functions.UserFunctionDef;\nimport edu.harvard.seas.pl.formulog.symbols.*;\nimport edu.harvard.seas.pl.formulog.symbols.parameterized.Param;\nimport edu.harvard.seas.pl.formulog.symbols.parameterized.ParameterizedConstructorSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.parameterized.ParameterizedSymbol;\nimport edu.harvard.seas.pl.formulog.types.Types.*;\nimport edu.harvard.seas.pl.formulog.unification.SimpleSubstitution;\nimport edu.harvard.seas.pl.formulog.unification.Substitution;\nimport edu.harvard.seas.pl.formulog.util.Triple;\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport java.util.*;\nimport java.util.concurrent.*;\nimport java.util.stream.Collectors;\n\npublic class TypeChecker {\n\n  private final Program<UserPredicate, BasicRule> prog;\n  private WellTypedProgram outputProgram;\n\n  public TypeChecker(Program<UserPredicate, BasicRule> prog2) {\n    this.prog = prog2;\n  }\n\n  public synchronized WellTypedProgram typeCheck() throws TypeException {\n    if (outputProgram != null) {\n      return outputProgram;\n    }\n    ExecutorService exec = Executors.newFixedThreadPool(Main.parallelism);\n    Map<RelationSymbol, Set<Term[]>> newFacts = typeCheckFacts(exec);\n    Map<FunctionSymbol, FunctionDef> newFuncs = typeCheckFunctions(exec);\n    FunctionDefManager dm = prog.getFunctionCallFactory().getDefManager();\n    for (FunctionDef func : newFuncs.values()) {\n      dm.reregister(func);\n    }\n    Map<RelationSymbol, Set<BasicRule>> newRules = typeCheckRules(exec);\n    UserPredicate newQuery = typeCheckQuery();\n    exec.shutdown();\n    outputProgram =\n        new WellTypedProgram() {\n\n          @Override\n          public Set<FunctionSymbol> getFunctionSymbols() {\n            return dm.getFunctionSymbols();\n          }\n\n          @Override\n          public Set<RelationSymbol> getFactSymbols() {\n            return Collections.unmodifiableSet(newFacts.keySet());\n          }\n\n          @Override\n          public Set<RelationSymbol> getRuleSymbols() {\n            return Collections.unmodifiableSet(newRules.keySet());\n          }\n\n          @Override\n          public FunctionDef getDef(FunctionSymbol sym) {\n            return dm.lookup(sym);\n          }\n\n          @Override\n          public Set<Term[]> getFacts(RelationSymbol sym) {\n            if (!sym.isEdbSymbol()) {\n              throw new IllegalArgumentException();\n            }\n            if (!newFacts.containsKey(sym)) {\n              throw new IllegalArgumentException();\n            }\n            return newFacts.get(sym);\n          }\n\n          @Override\n          public Set<BasicRule> getRules(RelationSymbol sym) {\n            if (!sym.isIdbSymbol()) {\n              throw new IllegalArgumentException();\n            }\n            if (!newRules.containsKey(sym)) {\n              throw new IllegalArgumentException();\n            }\n            return newRules.get(sym);\n          }\n\n          @Override\n          public SymbolManager getSymbolManager() {\n            return prog.getSymbolManager();\n          }\n\n          @Override\n          public boolean hasQuery() {\n            return newQuery != null;\n          }\n\n          @Override\n          public UserPredicate getQuery() {\n            return newQuery;\n          }\n\n          @Override\n          public FunctionCallFactory getFunctionCallFactory() {\n            return prog.getFunctionCallFactory();\n          }\n\n          @Override\n          public Set<ConstructorSymbol> getUninterpretedFunctionSymbols() {\n            return prog.getUninterpretedFunctionSymbols();\n          }\n\n          @Override\n          public Set<TypeSymbol> getTypeSymbols() {\n            return prog.getTypeSymbols();\n          }\n        };\n    return outputProgram;\n  }\n\n  private UserPredicate typeCheckQuery() throws TypeException {\n    if (prog.hasQuery()) {\n      TypeCheckerContext ctx = new TypeCheckerContext();\n      return ctx.typeCheckQuery(prog.getQuery());\n    }\n    return null;\n  }\n\n  private static <K, V> Map<K, V> mapFromFutures(Map<K, Future<V>> futures) throws TypeException {\n    try {\n      return Util.fillMapWithFutures(futures, new HashMap<>());\n    } catch (InterruptedException | ExecutionException e) {\n      throw new TypeException(e);\n    }\n  }\n\n  private Map<RelationSymbol, Set<Term[]>> typeCheckFacts(ExecutorService exec)\n      throws TypeException {\n    Map<RelationSymbol, Future<Set<Term[]>>> futures = new HashMap<>();\n    for (RelationSymbol sym : prog.getFactSymbols()) {\n      Future<Set<Term[]>> fut =\n          exec.submit(\n              new Callable<Set<Term[]>>() {\n\n                @Override\n                public Set<Term[]> call() throws Exception {\n                  Set<Term[]> s = new HashSet<>();\n                  TypeCheckerContext ctx = new TypeCheckerContext();\n                  for (Term[] args : prog.getFacts(sym)) {\n                    s.add(ctx.typeCheckFact(sym, args));\n                  }\n                  return s;\n                }\n              });\n      futures.put(sym, fut);\n    }\n    return mapFromFutures(futures);\n  }\n\n  private Map<RelationSymbol, Set<BasicRule>> typeCheckRules(ExecutorService exec)\n      throws TypeException {\n    Map<RelationSymbol, Future<Set<BasicRule>>> futures = new HashMap<>();\n    for (RelationSymbol sym : prog.getRuleSymbols()) {\n      Future<Set<BasicRule>> fut =\n          exec.submit(\n              new Callable<Set<BasicRule>>() {\n\n                @Override\n                public Set<BasicRule> call() throws Exception {\n                  Set<BasicRule> s = new HashSet<>();\n                  for (BasicRule r : prog.getRules(sym)) {\n                    TypeCheckerContext ctx = new TypeCheckerContext();\n                    s.add(ctx.typeCheckRule(r));\n                  }\n                  return s;\n                }\n              });\n      futures.put(sym, fut);\n    }\n    return mapFromFutures(futures);\n  }\n\n  private Map<FunctionSymbol, FunctionDef> typeCheckFunctions(ExecutorService exec)\n      throws TypeException {\n    Map<FunctionSymbol, Future<FunctionDef>> futures = new HashMap<>();\n    List<FunctionDef> nonUserFunctions = new ArrayList<>();\n    for (FunctionSymbol sym : prog.getFunctionSymbols()) {\n      FunctionDef def = prog.getDef(sym);\n      if (def instanceof UserFunctionDef) {\n        Future<FunctionDef> fut =\n            exec.submit(\n                () -> {\n                  TypeCheckerContext ctx = new TypeCheckerContext();\n                  try {\n                    return ctx.typeCheckFunction((UserFunctionDef) def);\n                  } catch (Throwable e) {\n                    throw new TypeException(\n                        \"Problem type checking function: \" + sym + \"\\n\" + e.getMessage());\n                  }\n                });\n        futures.put(sym, fut);\n      } else {\n        nonUserFunctions.add(def);\n      }\n    }\n    Map<FunctionSymbol, FunctionDef> m = mapFromFutures(futures);\n    for (FunctionDef def : nonUserFunctions) {\n      m.put(def.getSymbol(), def);\n    }\n    return m;\n  }\n\n  private class TypeCheckerContext {\n\n    private final Deque<Triple<Term, Type, Type>> constraints = new ArrayDeque<>();\n    private final Deque<Triple<Term, Type, Type>> formulaConstraints = new ArrayDeque<>();\n    private final Map<TypeVar, Type> typeVars = new HashMap<>();\n    private String error;\n\n    private Term rewriteTerm(Term t, Substitution m) throws TypeException {\n      return t.accept(termRewriter, m);\n    }\n\n    public Term[] typeCheckFact(RelationSymbol sym, Term[] args) throws TypeException {\n      Map<Var, Type> subst = new HashMap<>();\n      genConstraints(sym, args, subst);\n      if (!checkConstraints()) {\n        throw new TypeException(\n            \"Type error in fact: \" + UserPredicate.make(sym, args, false) + \"\\n\" + error);\n      }\n      Substitution m = makeIndexSubstitution(subst);\n      try {\n        return Terms.mapExn(args, t -> rewriteTerm(t, m));\n      } catch (TypeException e) {\n        throw new TypeException(\n            \"Problem with rewriting fact: \"\n                + UserPredicate.make(sym, args, false)\n                + \"\\n\"\n                + e.getMessage());\n      }\n    }\n\n    private ComplexLiteral rewriteLiteral(ComplexLiteral l, Substitution m, Map<Var, Type> env)\n        throws TypeException {\n      try {\n        Term[] args = Terms.mapExn(l.getArgs(), t -> rewriteTerm(t, m));\n        return l.accept(\n            new ComplexLiteralExnVisitor<Void, ComplexLiteral, TypeException>() {\n\n              @Override\n              public ComplexLiteral visit(UnificationPredicate pred, Void input)\n                  throws TypeException {\n                return UnificationPredicate.make(args[0], args[1], pred.isNegated());\n              }\n\n              @Override\n              public ComplexLiteral visit(UserPredicate pred, Void input) throws TypeException {\n                return UserPredicate.make(pred.getSymbol(), args, pred.isNegated());\n              }\n            },\n            null);\n      } catch (TypeException e) {\n        throw new TypeException(\"Problem with rewriting literal: \" + l + \"\\n\" + e.getMessage());\n      }\n    }\n\n    public UserPredicate typeCheckQuery(UserPredicate q) throws TypeException {\n      Map<Var, Type> subst = new HashMap<>();\n      genConstraints(q, subst);\n      if (!checkConstraints()) {\n        throw new TypeException(\"Type error in query: \" + q + \"\\n\" + error);\n      }\n      Substitution m = makeIndexSubstitution(subst);\n      try {\n        return (UserPredicate) rewriteLiteral(q, m, subst);\n      } catch (TypeException e) {\n        throw new TypeException(\"Problem with rewriting query: \" + q + \"\\n\" + e.getMessage());\n      }\n    }\n\n    public BasicRule typeCheckRule(Rule<UserPredicate, ComplexLiteral> r) throws TypeException {\n      Map<Var, Type> subst = new HashMap<>();\n      processAtoms(r, subst);\n      genConstraints(r.getHead(), subst);\n      if (!checkConstraints()) {\n        String msg = \"Type error in rule:\\n\";\n        msg += r + \"\\n\";\n        msg += error;\n        throw new TypeException(msg);\n      }\n      Substitution m = makeIndexSubstitution(subst);\n      UserPredicate newHead = (UserPredicate) rewriteLiteral(r.getHead(), m, subst);\n      try {\n        List<ComplexLiteral> newBody = new ArrayList<>();\n        for (ComplexLiteral a : r) {\n          newBody.add(rewriteLiteral(a, m, subst));\n        }\n        return BasicRule.make(newHead, newBody);\n      } catch (TypeException e) {\n        throw new TypeException(\"Problem with rewriting rule:\\n\" + r + \"\\n\" + e.getMessage());\n      }\n    }\n\n    private Substitution makeIndexSubstitution(Map<Var, Type> subst) {\n      Substitution m = new SimpleSubstitution();\n      for (Map.Entry<Var, Type> e : subst.entrySet()) {\n        Var x = e.getKey();\n        Type t = lookupType(e.getValue());\n        if (t instanceof TypeIndex) {\n          int idx = ((TypeIndex) t).getIndex();\n          ConstructorSymbol csym = prog.getSymbolManager().lookupIndexConstructorSymbol(idx);\n          Term c = Constructors.make(csym, Terms.singletonArray(I32.make(idx)));\n          m.put(x, c);\n        }\n      }\n      return m;\n    }\n\n    public UserFunctionDef typeCheckFunction(UserFunctionDef functionDef) throws TypeException {\n      Map<Var, Type> subst = new HashMap<>();\n      genConstraints(functionDef, subst);\n      if (!checkConstraints()) {\n        throw new TypeException(\n            \"Type error in function: \"\n                + functionDef.getSymbol()\n                + \"\\n\"\n                + functionDef.getBody()\n                + \"\\n\"\n                + error);\n      }\n      Substitution m = makeIndexSubstitution(subst);\n      try {\n        return UserFunctionDef.get(\n            functionDef.getSymbol(),\n            functionDef.getParams(),\n            rewriteTerm(functionDef.getBody(), m));\n      } catch (Throwable e) {\n        throw new TypeException(\n            \"Problem with rewriting the function: \"\n                + functionDef.getSymbol()\n                + \"\\n\"\n                + functionDef.getBody()\n                + \"\\n\"\n                + e.getMessage());\n      }\n    }\n\n    private void processAtoms(Iterable<ComplexLiteral> atoms, Map<Var, Type> subst) {\n      for (ComplexLiteral a : atoms) {\n        genConstraints(a, subst);\n      }\n    }\n\n    private void genConstraints(RelationSymbol sym, Term[] args, Map<Var, Type> subst) {\n      FunctorType ftype = sym.getCompileTimeType().freshen();\n      assert ftype.getArgTypes().size() == args.length;\n      int i = 0;\n      for (Type type : ftype.getArgTypes()) {\n        genConstraints(args[i], type, subst, false);\n        ++i;\n      }\n    }\n\n    private void genConstraints(ComplexLiteral a, Map<Var, Type> subst) {\n      a.accept(\n          new ComplexLiteralVisitor<Void, Void>() {\n\n            @Override\n            public Void visit(UserPredicate normalAtom, Void in) {\n              genConstraints(normalAtom.getSymbol(), normalAtom.getArgs(), subst);\n              return null;\n            }\n\n            @Override\n            public Void visit(UnificationPredicate unifyAtom, Void in) {\n              TypeVar var = TypeVar.fresh();\n              genConstraints(unifyAtom.getLhs(), var, subst, false);\n              genConstraints(unifyAtom.getRhs(), var, subst, false);\n              return null;\n            }\n          },\n          null);\n    }\n\n    private void genConstraints(UserFunctionDef def, Map<Var, Type> subst) {\n      FunctionSymbol sym = def.getSymbol();\n      genConstraints(sym, def.getParams(), def.getBody(), subst);\n    }\n\n    private void genConstraints(\n        FunctionSymbol sym, List<Var> params, Term body, Map<Var, Type> subst) {\n      FunctorType scheme = sym.getCompileTimeType().freshen();\n      Map<TypeVar, OpaqueType> opaqueTypes = new HashMap<>();\n      List<Type> argTypes = scheme.getArgTypes();\n      for (Type t : argTypes) {\n        addConstraint(null, t, mkTypeVarsOpaque(t, opaqueTypes), false);\n      }\n      Type r = scheme.getRetType();\n      addConstraint(null, r, mkTypeVarsOpaque(r, opaqueTypes), false);\n      for (int i = 0; i < params.size(); ++i) {\n        subst.put(params.get(i), argTypes.get(i));\n      }\n      genConstraints(body, r, subst, false);\n    }\n\n    private Type mkTypeVarsOpaque(Type t, Map<TypeVar, OpaqueType> subst) {\n      return t.accept(\n          new TypeVisitor<Void, Type>() {\n            @Override\n            public Type visit(TypeVar typeVar, Void in) {\n              return Util.lookupOrCreate(subst, typeVar, OpaqueType::get);\n            }\n\n            @Override\n            public Type visit(AlgebraicDataType algebraicType, Void in) {\n              List<Type> newArgs =\n                  algebraicType.getTypeArgs().stream()\n                      .map(t -> t.accept(this, null))\n                      .collect(Collectors.toList());\n              return AlgebraicDataType.make(algebraicType.getSymbol(), newArgs);\n            }\n\n            @Override\n            public Type visit(OpaqueType opaqueType, Void in) {\n              return opaqueType;\n            }\n\n            @Override\n            public Type visit(TypeIndex typeIndex, Void in) {\n              return typeIndex;\n            }\n          },\n          null);\n    }\n\n    private void genConstraintsForExpr(\n        Expr e, Type exprType, Map<Var, Type> varTypes, boolean inFormula) {\n      e.accept(\n          new ExprVisitor<Void, Void>() {\n\n            @Override\n            public Void visit(MatchExpr matchExpr, Void in) {\n              assert !inFormula;\n              TypeVar guardType = TypeVar.fresh();\n              Term guard = matchExpr.getMatchee();\n              genConstraints(guard, guardType, varTypes, inFormula);\n              for (MatchClause cl : matchExpr) {\n                genConstraints(cl.getLhs(), guardType, varTypes, inFormula);\n                genConstraints(cl.getRhs(), exprType, varTypes, inFormula);\n              }\n              return null;\n            }\n\n            @Override\n            public Void visit(FunctionCall funcCall, Void in) {\n              genConstraintsForFunctionCall(funcCall, exprType, varTypes, inFormula);\n              return null;\n            }\n\n            @Override\n            public Void visit(LetFunExpr letFun, Void in) {\n              assert !inFormula;\n              for (NestedFunctionDef def : letFun.getDefs()) {\n                def = def.freshen();\n                genConstraints(def.getSymbol(), def.getParams(), def.getBody(), varTypes);\n              }\n              genConstraints(letFun.getLetBody(), exprType, varTypes, inFormula);\n              return null;\n            }\n\n            @Override\n            public Void visit(Fold fold, Void in) {\n              assert !inFormula;\n              FunctionSymbol sym = fold.getFunction();\n              Term[] args = fold.getArgs();\n              assert sym.getArity() == args.length && args.length == 2;\n              Type itemType = TypeVar.fresh();\n              Type listType = BuiltInTypes.list(itemType);\n              FunctorType funType = sym.getCompileTimeType().freshen();\n              genConstraints(args[0], exprType, varTypes, inFormula);\n              genConstraints(args[0], funType.getArgTypes().get(0), varTypes, inFormula);\n              genConstraints(args[1], listType, varTypes, inFormula);\n              addConstraint(args[1], itemType, funType.getArgTypes().get(1), inFormula);\n              addConstraint(fold, exprType, funType.getRetType(), inFormula);\n              return null;\n            }\n          },\n          null);\n    }\n\n    private void addConstraint(Term t, Type t1, Type t2, boolean inFormula) {\n      Triple<Term, Type, Type> constraint = new Triple<>(t, t1, t2);\n      if (inFormula) {\n        formulaConstraints.add(constraint);\n      } else {\n        constraints.add(constraint);\n      }\n    }\n\n    private void genConstraints(Term t, Type ttype, Map<Var, Type> subst, boolean inFormula) {\n      t.accept(\n          new TermVisitor<Void, Void>() {\n\n            @Override\n            public Void visit(Var t, Void in) {\n              genConstraintsForVar(t, ttype, subst, inFormula);\n              return null;\n            }\n\n            @Override\n            public Void visit(Constructor t, Void in) {\n              genConstraintsForConstructor(t, ttype, subst, inFormula);\n              return null;\n            }\n\n            @Override\n            public Void visit(Primitive<?> prim, Void in) {\n              genConstraintsForPrimitive(prim, ttype, inFormula);\n              return null;\n            }\n\n            @Override\n            public Void visit(Expr expr, Void in) {\n              genConstraintsForExpr(expr, ttype, subst, inFormula);\n              return null;\n            }\n          },\n          null);\n    }\n\n    private void genConstraintsForVar(Var t, Type ttype, Map<Var, Type> subst, boolean inFormula) {\n      Type s =\n          Util.lookupOrCreate(\n              subst,\n              t,\n              () -> {\n                Type x = TypeVar.fresh();\n                if (inFormula) {\n                  x = BuiltInTypes.smt(x);\n                }\n                return x;\n              });\n      addConstraint(t, s, ttype, inFormula);\n    }\n\n    private void genConstraintsForPrimitive(Primitive<?> t, Type ttype, boolean inFormula) {\n      addConstraint(t, ttype, t.getType().freshen(), inFormula);\n    }\n\n    private void genConstraintsForConstructor(\n        Constructor t, Type ttype, Map<Var, Type> subst, boolean inFormula) {\n      boolean wasInFormula = inFormula;\n      ConstructorSymbol cnstrSym = t.getSymbol();\n      if (cnstrSym.equals(BuiltInConstructorSymbol.ENTER_FORMULA)) {\n        assert !wasInFormula;\n        inFormula = true;\n      }\n      if (cnstrSym.equals(BuiltInConstructorSymbol.EXIT_FORMULA)) {\n        inFormula = false;\n      }\n      FunctorType cnstrType = cnstrSym.getCompileTimeType();\n      if (!(cnstrSym instanceof ParameterizedConstructorSymbol)) {\n        cnstrType = cnstrType.freshen();\n      }\n      Term[] args = t.getArgs();\n      List<Type> argTypes = cnstrType.getArgTypes();\n      for (int i = 0; i < args.length; ++i) {\n        Type argType = argTypes.get(i);\n        genConstraints(args[i], argType, subst, inFormula);\n      }\n      addConstraint(t, cnstrType.getRetType(), ttype, wasInFormula);\n    }\n\n    private void genConstraintsForFunctionCall(\n        FunctionCall function, Type ttype, Map<Var, Type> subst, boolean inFormula) {\n      FunctorType funType = (FunctorType) function.getSymbol().getCompileTimeType().freshen();\n      Term[] args = function.getArgs();\n      List<Type> argTypes = funType.getArgTypes();\n      for (int i = 0; i < args.length; ++i) {\n        genConstraints(args[i], argTypes.get(i), subst, inFormula);\n      }\n      addConstraint(function, funType.getRetType(), ttype, inFormula);\n    }\n\n    private boolean checkConstraints() {\n      Set<Type> typesInFormulae = new HashSet<>();\n      for (Triple<Term, Type, Type> p : formulaConstraints) {\n        typesInFormulae.add(p.second);\n      }\n      // First try to unify constraints generated outside of formula and then\n      // constraints generated inside formula. This might generate more constraints,\n      // but all of them will be treated as being in a non-formula context.\n      boolean ok = checkConstraints(false) && checkConstraints(true) && checkConstraints(false);\n      assert !ok || constraints.isEmpty() && formulaConstraints.isEmpty();\n      ok = ok && checkTypesInFormulae(typesInFormulae);\n      return ok;\n    }\n\n    private boolean checkTypesInFormulae(Set<Type> types) {\n      for (Type ty : types) {\n        if (!checkTypeInFormula(ty)) {\n          return false;\n        }\n      }\n      return true;\n    }\n\n    private boolean checkTypeInFormula(Type ty) {\n      ty = lookupType(ty);\n      if (!Types.isSmtRepresentable(ty)) {\n        error =\n            \"Terms of the following type are not allowed in formulas: \"\n                + ty\n                + \"\\n\"\n                + \"The following types can lead to this error: model, smt_pattern, smt_wrapped_var,\"\n                + \" and type variables that are not proven to contain a safe type\";\n        return false;\n      }\n      return true;\n    }\n\n    private boolean checkConstraints(boolean inFormulaContext) {\n      Deque<Triple<Term, Type, Type>> q = inFormulaContext ? formulaConstraints : constraints;\n      while (!q.isEmpty()) {\n        Triple<Term, Type, Type> constraint = q.pop();\n        Type type1 = lookupType(constraint.second);\n        Type type2 = lookupType(constraint.third);\n        if (inFormulaContext) {\n          type1 = simplify(type1);\n          type2 = simplify(type2);\n        }\n        if (type1.isVar() || type2.isVar()) {\n          if (!handleVars(type1, type2)) {\n            return false;\n          }\n        } else if (!unify(constraint.first, type1, type2)) {\n          error = \"Cannot unify \" + type1 + \" and \" + type2;\n          error += \"\\nProblematic term: \" + constraint.first;\n          return false;\n        }\n      }\n      return true;\n    }\n\n    private boolean handleVars(Type type1, Type type2) {\n      if (type1.isVar() && type2.isVar()) {\n        addBinding((TypeVar) type1, type2, typeVars);\n        return true;\n      }\n\n      TypeVar var;\n      Type other;\n      if (type1.isVar()) {\n        var = (TypeVar) type1;\n        other = type2;\n      } else {\n        assert type2.isVar();\n        var = (TypeVar) type2;\n        other = type1;\n      }\n      // Occurs check\n      if (Types.getTypeVars(other).contains(var)) {\n        return false;\n      }\n      typeVars.put(var, other);\n      return true;\n    }\n\n    private boolean unify(Term t, Type type1, Type type2) {\n      return type1.accept(\n          new TypeVisitor<Type, Boolean>() {\n\n            @Override\n            public Boolean visit(TypeVar typeVar, Type other) {\n              throw new AssertionError(\"unreachable\");\n            }\n\n            @Override\n            public Boolean visit(AlgebraicDataType typeRef, Type other) {\n              if (!(other instanceof AlgebraicDataType)) {\n                return false;\n              }\n              AlgebraicDataType otherTypeRef = (AlgebraicDataType) other;\n              if (!typeRef.getSymbol().equals(otherTypeRef.getSymbol())) {\n                return false;\n              }\n              List<Type> args1 = typeRef.getTypeArgs();\n              List<Type> args2 = otherTypeRef.getTypeArgs();\n              for (int i = 0; i < args1.size(); ++i) {\n                addConstraint(t, args1.get(i), args2.get(i), false);\n              }\n              return true;\n            }\n\n            @Override\n            public Boolean visit(OpaqueType opaqueType, Type other) {\n              return opaqueType.equals(other);\n            }\n\n            @Override\n            public Boolean visit(TypeIndex typeIndex, Type other) {\n              return typeIndex.equals(other);\n            }\n          },\n          type2);\n    }\n\n    private Type lookupType(Type t) {\n      return TypeChecker.lookupType(t, typeVars);\n    }\n\n    private final TermVisitorExn<Substitution, Term, TypeException> termRewriter =\n        new TermVisitorExn<Substitution, Term, TypeException>() {\n\n          @Override\n          public Term visit(Var x, Substitution subst) throws TypeException {\n            if (subst.containsKey(x)) {\n              return subst.get(x);\n            }\n            return x;\n          }\n\n          @Override\n          public Term visit(Constructor c, Substitution subst) throws TypeException {\n            ConstructorSymbol sym = c.getSymbol();\n            if (sym.equals(BuiltInConstructorSymbol.ENTER_FORMULA)\n                || sym.equals(BuiltInConstructorSymbol.EXIT_FORMULA)) {\n              return c.getArgs()[0].accept(this, subst);\n            }\n            if (sym instanceof ParameterizedConstructorSymbol) {\n              try {\n                sym =\n                    (ConstructorSymbol)\n                        handleParameterizedSymbol((ParameterizedConstructorSymbol) sym);\n              } catch (TypeException e) {\n                throw new TypeException(\n                    \"Problem with rewriting term \" + c + \":\\n\" + e.getMessage());\n              }\n            }\n            Term[] args = c.getArgs();\n            Term[] newArgs = new Term[args.length];\n            for (int i = 0; i < args.length; ++i) {\n              newArgs[i] = args[i].accept(this, subst);\n            }\n            return Constructors.make(sym, newArgs);\n          }\n\n          @Override\n          public Term visit(Primitive<?> p, Substitution subst) throws TypeException {\n            return p;\n          }\n\n          @Override\n          public Term visit(Expr e, Substitution subst) throws TypeException {\n            return e.accept(exprRewriter, subst);\n          }\n        };\n\n    private ParameterizedSymbol handleParameterizedSymbol(ParameterizedSymbol sym)\n        throws TypeException {\n      List<Param> params = sym.getArgs();\n      params = Param.applySubst(params, typeVars);\n      for (Param param : params) {\n        if (Types.containsTypeVarOrOpaqueType(param.getType())) {\n          throw new TypeException(\n              \"Cannot instantiate parameterized symbol \"\n                  + sym\n                  + \" with a parameter that is not ground: \"\n                  + param);\n        }\n      }\n      return sym.copyWithNewArgs(params).makeFinal();\n    }\n\n    private final Map<FunctionSymbol, FunctionSymbol> topLevelSymbolOfNestedFunction =\n        new HashMap<>();\n    private final Map<FunctionSymbol, List<Var>> capturedVarsOfNestedFunction = new HashMap<>();\n\n    private final ExprVisitorExn<Substitution, Term, TypeException> exprRewriter =\n        new ExprVisitorExn<Substitution, Term, TypeException>() {\n\n          @Override\n          public Term visit(MatchExpr matchExpr, Substitution subst) throws TypeException {\n            Term scrutinee = matchExpr.getMatchee().accept(termRewriter, subst);\n            List<MatchClause> clauses = new ArrayList<>();\n            for (MatchClause cl : matchExpr) {\n              Term lhs = cl.getLhs().accept(termRewriter, subst);\n              Term rhs = cl.getRhs().accept(termRewriter, subst);\n              clauses.add(MatchClause.make(lhs, rhs));\n            }\n            return MatchExpr.make(scrutinee, clauses);\n          }\n\n          @Override\n          public Term visit(FunctionCall funcCall, Substitution subst) throws TypeException {\n            FunctionSymbol sym = funcCall.getSymbol();\n            List<Var> capturedVars = Collections.emptyList();\n            if (topLevelSymbolOfNestedFunction.containsKey(sym)) {\n              sym = topLevelSymbolOfNestedFunction.get(sym);\n              capturedVars = capturedVarsOfNestedFunction.get(sym);\n            }\n            Term[] args = funcCall.getArgs();\n            Term[] newArgs = new Term[args.length + capturedVars.size()];\n            int i = 0;\n            for (; i < args.length; ++i) {\n              newArgs[i] = args[i].accept(termRewriter, subst);\n            }\n            for (Var x : capturedVars) {\n              newArgs[i] = x;\n              ++i;\n            }\n            return prog.getFunctionCallFactory().make(sym, newArgs);\n          }\n\n          @Override\n          public Term visit(LetFunExpr letFun, Substitution in) throws TypeException {\n            Set<NestedFunctionDef> defs = new HashSet<>();\n            Set<Var> capturedVarSet = new HashSet<>();\n            for (NestedFunctionDef def : letFun.getDefs()) {\n              def = firstRewritingPass(def, in);\n              defs.add(def);\n              Set<Var> s = def.getBody().varSet();\n              s.removeAll(def.getParams());\n              capturedVarSet.addAll(s);\n            }\n            List<Var> capturedVars = new ArrayList<>(capturedVarSet);\n            for (NestedFunctionDef def : defs) {\n              FunctionSymbol oldSym = def.getSymbol();\n              int newArity = oldSym.getArity() + capturedVarSet.size();\n              FunctionSymbol newSym =\n                  prog.getSymbolManager()\n                      .createFunctionSymbol(oldSym + \"$toplevel\", newArity, null);\n              topLevelSymbolOfNestedFunction.put(oldSym, newSym);\n              capturedVarsOfNestedFunction.put(newSym, capturedVars);\n            }\n            for (NestedFunctionDef def : defs) {\n              FunctionSymbol sym = topLevelSymbolOfNestedFunction.get(def.getSymbol());\n              List<Var> params = new ArrayList<>(def.getParams());\n              params.addAll(capturedVars);\n              FunctionDef newDef =\n                  UserFunctionDef.get(sym, params, def.getBody().accept(termRewriter, in));\n              prog.getFunctionCallFactory().getDefManager().register(newDef);\n            }\n            return letFun.getLetBody().accept(termRewriter, in);\n          }\n\n          NestedFunctionDef firstRewritingPass(NestedFunctionDef def, Substitution subst)\n              throws TypeException {\n            Substitution s = new SimpleSubstitution();\n            List<Var> newParams = new ArrayList<>();\n            for (Var param : def.getParams()) {\n              if (!s.containsKey(param)) {\n                Var newParam = Var.fresh(param.toString());\n                s.put(param, newParam);\n                newParams.add(newParam);\n              } else {\n                newParams.add((Var) s.get(param));\n              }\n            }\n            Term body = def.getBody().applySubstitution(s).accept(termRewriter, subst);\n            return NestedFunctionDef.make(def.getSymbol(), newParams, body);\n          }\n\n          @Override\n          public Term visit(Fold fold, Substitution in) throws TypeException {\n            FunctionCall f = (FunctionCall) fold.getShamCall().accept(this, in);\n            return Fold.mk(f.getSymbol(), f.getArgs(), f.getFactory());\n          }\n        };\n  }\n\n  // XXX This and lookupType should be factored into a class for type\n  // substitutions.\n  public static void addBinding(TypeVar x, Type t, Map<TypeVar, Type> subst) {\n    if (t instanceof TypeVar) {\n      // Avoid cycles in map\n      TypeVar y = (TypeVar) t;\n      if (x.compareTo(y) > 0) {\n        subst.put(x, y);\n      } else if (x.compareTo(y) < 0) {\n        subst.put(y, x);\n      }\n    } else {\n      subst.put(x, t);\n    }\n  }\n\n  public static Type lookupType(Type t, Map<TypeVar, ? extends Type> subst) {\n    return t.accept(\n        new TypeVisitor<Void, Type>() {\n\n          @Override\n          public Type visit(TypeVar typeVar, Void in) {\n            if (subst.containsKey(typeVar)) {\n              return subst.get(typeVar).accept(this, in);\n            }\n            return typeVar;\n          }\n\n          @Override\n          public Type visit(AlgebraicDataType algebraicType, Void in) {\n            List<Type> args = Util.map(algebraicType.getTypeArgs(), t -> t.accept(this, in));\n            return AlgebraicDataType.make(algebraicType.getSymbol(), args);\n          }\n\n          @Override\n          public Type visit(OpaqueType opaqueType, Void in) {\n            return opaqueType;\n          }\n\n          @Override\n          public Type visit(TypeIndex typeIndex, Void in) {\n            return typeIndex;\n          }\n        },\n        null);\n  }\n\n  // Simplify type: make it a non-smt, non-sym type.\n  public static Type simplify(Type t) {\n    return t.accept(\n        new TypeVisitor<Void, Type>() {\n\n          @Override\n          public Type visit(TypeVar typeVar, Void in) {\n            return typeVar;\n          }\n\n          @Override\n          public Type visit(AlgebraicDataType algebraicType, Void in) {\n            TypeSymbol sym = algebraicType.getSymbol();\n            List<Type> args = algebraicType.getTypeArgs();\n            if (sym.equals(BuiltInTypeSymbol.SMT_TYPE) || sym.equals(BuiltInTypeSymbol.SYM_TYPE)) {\n              return args.get(0).accept(this, in);\n            }\n            args = Util.map(args, t -> t.accept(this, in));\n            return AlgebraicDataType.make(sym, args);\n          }\n\n          @Override\n          public Type visit(OpaqueType opaqueType, Void in) {\n            return opaqueType;\n          }\n\n          @Override\n          public Type visit(TypeIndex typeIndex, Void in) {\n            return typeIndex;\n          }\n        },\n        null);\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/types/TypeException.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.types;\n\npublic class TypeException extends Exception {\n\n  private static final long serialVersionUID = -7218535057319510121L;\n\n  /** Constructs an exception signifying a type error. */\n  public TypeException() {}\n\n  /**\n   * Constructs an exception signifying a type error.\n   *\n   * @param message the error message\n   */\n  public TypeException(String message) {\n    super(message);\n  }\n\n  /**\n   * Constructs an exception signifying a type error.\n   *\n   * @param cause the exception that caused this exception\n   */\n  public TypeException(Throwable cause) {\n    super(cause);\n  }\n\n  /**\n   * Constructs an exception signifying a type error.\n   *\n   * @param message the error message\n   * @param cause the exception that caused this exception\n   */\n  public TypeException(String message, Throwable cause) {\n    super(message, cause);\n  }\n\n  /**\n   * Constructs an exception signifying a type error.\n   *\n   * @param message the error message\n   * @param cause the exception that caused this exception\n   * @param enableSuppression whether or not suppression is enabled or disabled\n   * @param writableStackTrace whether or not the stack trace should be writable\n   */\n  public TypeException(\n      String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {\n    super(message, cause, enableSuppression, writableStackTrace);\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/types/TypeManager.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.types;\n\nimport edu.harvard.seas.pl.formulog.symbols.TypeSymbol;\nimport edu.harvard.seas.pl.formulog.types.Types.AlgebraicDataType;\nimport edu.harvard.seas.pl.formulog.types.Types.Type;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.NoSuchElementException;\n\npublic class TypeManager {\n\n  private final Map<TypeSymbol, TypeAlias> aliases = new HashMap<>();\n\n  public void registerAlias(TypeAlias alias) {\n    TypeAlias alias2 = aliases.putIfAbsent(alias.getSymbol(), alias);\n    if (alias2 != null && !alias2.equals(alias)) {\n      throw new IllegalArgumentException(\n          \"Cannot register \" + alias.getSymbol() + \" as aliasing multiple types.\");\n    }\n  }\n\n  public Type lookup(TypeSymbol typeSym, List<Type> typeArgs) {\n    if (!typeSym.isAlias()) {\n      return AlgebraicDataType.make(typeSym, typeArgs);\n    }\n    TypeAlias alias = aliases.get(typeSym);\n    if (alias == null) {\n      throw new NoSuchElementException(\"No type associated with symbol \" + typeSym);\n    }\n    return alias.instantiate(typeArgs);\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/types/Types.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.types;\n\nimport edu.harvard.seas.pl.formulog.symbols.BuiltInTypeSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.ConstructorSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.GlobalSymbolManager;\nimport edu.harvard.seas.pl.formulog.symbols.Symbol;\nimport edu.harvard.seas.pl.formulog.symbols.TypeSymbol;\nimport edu.harvard.seas.pl.formulog.util.Pair;\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.concurrent.atomic.AtomicReference;\nimport java.util.stream.Collectors;\n\npublic final class Types {\n\n  private Types() {\n    throw new AssertionError();\n  }\n\n  public interface Type {\n\n    <I, O> O accept(TypeVisitor<I, O> visitor, I in);\n\n    Type applySubst(Map<TypeVar, ? extends Type> subst);\n\n    Type freshen();\n\n    default boolean isVar() {\n      return false;\n    }\n\n    default boolean isIndex() {\n      return false;\n    }\n  }\n\n  public interface TypeVisitor<I, O> {\n\n    O visit(TypeVar typeVar, I in);\n\n    O visit(AlgebraicDataType algebraicType, I in);\n\n    O visit(OpaqueType opaqueType, I in);\n\n    O visit(TypeIndex typeIndex, I in);\n  }\n\n  public static class TypeVar implements Type, Comparable<TypeVar> {\n\n    private static final Map<String, Integer> memo = new ConcurrentHashMap<>();\n    private static final AtomicInteger cnt = new AtomicInteger();\n\n    private final int id;\n\n    private TypeVar(int id) {\n      this.id = id;\n    }\n\n    public static TypeVar get(String id) {\n      int i = Util.lookupOrCreate(memo, id, cnt::getAndIncrement);\n      return new TypeVar(i);\n    }\n\n    @Override\n    public String toString() {\n      return \"'_\" + id;\n    }\n\n    @Override\n    public <I, O> O accept(TypeVisitor<I, O> visitor, I in) {\n      return visitor.visit(this, in);\n    }\n\n    @Override\n    public int hashCode() {\n      final int prime = 31;\n      int result = 1;\n      result = prime * result + id;\n      return result;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n      if (this == obj) return true;\n      if (obj == null) return false;\n      if (getClass() != obj.getClass()) return false;\n      TypeVar other = (TypeVar) obj;\n      return id == other.id;\n    }\n\n    @Override\n    public Type applySubst(Map<TypeVar, ? extends Type> subst) {\n      return TypeChecker.lookupType(this, subst);\n    }\n\n    public static TypeVar fresh() {\n      return new TypeVar(cnt.getAndIncrement());\n    }\n\n    @Override\n    public boolean isVar() {\n      return true;\n    }\n\n    @Override\n    public int compareTo(TypeVar o) {\n      return Integer.compare(this.id, o.id);\n    }\n\n    @Override\n    public Type freshen() {\n      return fresh();\n    }\n  }\n\n  private static List<Type> freshen(List<Type> types) {\n    return types.stream().map(Type::freshen).collect(Collectors.toList());\n  }\n\n  public static class AlgebraicDataType implements Type, Iterable<Type> {\n\n    private final TypeSymbol sym;\n    private final List<Type> typeArgs;\n    private final AtomicReference<Set<ConstructorScheme>> constructors = new AtomicReference<>();\n\n    private static final Map<Symbol, Pair<List<TypeVar>, Set<ConstructorScheme>>> memo =\n        new ConcurrentHashMap<>();\n\n    private AlgebraicDataType(TypeSymbol sym, List<Type> typeArgs) {\n      this.sym = sym;\n      this.typeArgs = new ArrayList<>(typeArgs);\n      check();\n    }\n\n    private void check() {\n      if (sym.getArity() != typeArgs.size()) {\n        throw new IllegalArgumentException(\n            \"Arity of symbol \"\n                + sym\n                + \" (\"\n                + sym.getArity()\n                + \") does not match number of provided type parameters: \"\n                + typeArgs);\n      }\n      if (sym.isAlias()) {\n        throw new IllegalArgumentException(\"Cannot create a type with alias symbol \" + sym);\n      }\n      if (sym instanceof BuiltInTypeSymbol) {\n        switch ((BuiltInTypeSymbol) sym) {\n          case ARRAY_TYPE:\n          case BOOL_TYPE:\n          case CMP_TYPE:\n          case INT_TYPE:\n          case LIST_TYPE:\n          case MODEL_TYPE:\n          case OPTION_TYPE:\n          case SMT_PATTERN_TYPE:\n          case SMT_WRAPPED_VAR_TYPE:\n          case STRING_TYPE:\n          case OPAQUE_SET:\n            break;\n          case BV:\n          case FP:\n            for (Type arg : typeArgs) {\n              if (!arg.isVar() && !(arg instanceof TypeIndex)) {\n                throw new IllegalArgumentException(\n                    \"Cannot instantiate built-in type \" + sym + \" with type argument \" + arg);\n              }\n            }\n            break;\n          case SMT_TYPE:\n          case SYM_TYPE:\n            for (Type arg : typeArgs) {\n              if (!mayBePreSmtType(arg)) {\n                throw new IllegalArgumentException(\n                    \"Cannot instantiate built-in type \" + sym + \" with type argument \" + arg);\n              }\n            }\n            break;\n          default:\n            throw new AssertionError(\"impossible\");\n        }\n      }\n    }\n\n    public static AlgebraicDataType makeWithFreshArgs(TypeSymbol sym) {\n      List<Type> typeArgs = new ArrayList<>();\n      for (int i = 0; i < sym.getArity(); ++i) {\n        typeArgs.add(TypeVar.fresh());\n      }\n      return make(sym, typeArgs);\n    }\n\n    public static AlgebraicDataType make(TypeSymbol sym, Type... typeArgs) {\n      return make(sym, Arrays.asList(typeArgs));\n    }\n\n    public static AlgebraicDataType make(TypeSymbol sym, List<Type> typeArgs) {\n      return new AlgebraicDataType(sym, typeArgs);\n    }\n\n    public static void setConstructors(\n        TypeSymbol sym, List<TypeVar> typeParams, Collection<ConstructorScheme> constructors) {\n      if (sym.isAlias()) {\n        throw new IllegalArgumentException(\n            \"Cannot set constructors for type alias symbol \" + sym + \".\");\n      }\n      if (typeParams.size() != (new HashSet<>(typeParams).size())) {\n        throw new IllegalArgumentException(\"Each type variable must be unique.\");\n      }\n      if (memo.put(sym, new Pair<>(typeParams, new HashSet<>(constructors))) != null) {\n        throw new IllegalStateException(\"Cannot set the constructors for a type multiple times.\");\n      }\n    }\n\n    public boolean hasConstructors() {\n      return memo.containsKey(sym);\n    }\n\n    public Set<ConstructorScheme> getConstructors() {\n      Set<ConstructorScheme> s = constructors.get();\n      if (s == null) {\n        Pair<List<TypeVar>, Set<ConstructorScheme>> p = memo.get(sym);\n        if (p == null) {\n          throw new IllegalStateException(\"No constructors have been set for symbol \" + sym + \".\");\n        }\n        List<TypeVar> params = p.fst();\n        Map<TypeVar, Type> subst = new HashMap<>();\n        for (int i = 0; i < params.size(); ++i) {\n          TypeVar x = params.get(i);\n          Type t = typeArgs.get(i);\n          // XXX This is to avoid cycles in subst, but is obviously fragile\n          if (!x.equals(t)) {\n            subst.put(x, t);\n          }\n        }\n        s = new HashSet<>();\n        for (ConstructorScheme c : p.snd()) {\n          List<Type> newArgs = new ArrayList<>();\n          for (Type t : c.getTypeArgs()) {\n            newArgs.add(t.applySubst(subst));\n          }\n          s.add(new ConstructorScheme(c.getSymbol(), newArgs, c.getGetterSymbols()));\n        }\n        if (!constructors.compareAndSet(null, s)) {\n          s = constructors.get();\n        }\n      }\n      return s;\n    }\n\n    public TypeSymbol getSymbol() {\n      return sym;\n    }\n\n    @Override\n    public Iterator<Type> iterator() {\n      return typeArgs.iterator();\n    }\n\n    @Override\n    public String toString() {\n      if (sym.equals(BuiltInTypeSymbol.BV) || sym.equals(BuiltInTypeSymbol.FP)) {\n        return toStringBvOrFp();\n      }\n      if (typeArgs.isEmpty()) {\n        return sym.toString();\n      }\n      if (typeArgs.size() == 1) {\n        return typeArgs.get(0) + \" \" + sym;\n      }\n      StringBuilder sb = new StringBuilder(\"(\");\n      Iterator<Type> it = typeArgs.iterator();\n      sb.append(it.next());\n      while (it.hasNext()) {\n        sb.append(\", \").append(it.next());\n      }\n      sb.append(\") \").append(sym);\n      return sb.toString();\n    }\n\n    private String toStringBvOrFp() {\n      StringBuilder sb = new StringBuilder(sym.toString());\n      sb.append(\"[\");\n      for (Iterator<Type> it = typeArgs.iterator(); it.hasNext(); ) {\n        Type arg = it.next();\n        if (arg instanceof TypeIndex) {\n          sb.append(((TypeIndex) arg).getIndex());\n        } else {\n          assert arg.isVar();\n          sb.append(arg);\n        }\n        if (it.hasNext()) {\n          sb.append(\", \");\n        }\n      }\n      return sb + \"]\";\n    }\n\n    @Override\n    public <I, O> O accept(TypeVisitor<I, O> visitor, I in) {\n      return visitor.visit(this, in);\n    }\n\n    @Override\n    public int hashCode() {\n      final int prime = 31;\n      int result = 1;\n      result = prime * result + ((sym == null) ? 0 : sym.hashCode());\n      result = prime * result + ((typeArgs == null) ? 0 : typeArgs.hashCode());\n      return result;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n      if (this == obj) return true;\n      if (obj == null) return false;\n      if (getClass() != obj.getClass()) return false;\n      AlgebraicDataType other = (AlgebraicDataType) obj;\n      if (sym == null) {\n        if (other.sym != null) return false;\n      } else if (!sym.equals(other.sym)) return false;\n      if (typeArgs == null) {\n        return other.typeArgs == null;\n      } else return typeArgs.equals(other.typeArgs);\n    }\n\n    public List<Type> getTypeArgs() {\n      return typeArgs;\n    }\n\n    @Override\n    public Type applySubst(Map<TypeVar, ? extends Type> subst) {\n      List<Type> newTypes = new ArrayList<>();\n      for (Type t : typeArgs) {\n        newTypes.add(t.applySubst(subst));\n      }\n      return make(sym, newTypes);\n    }\n\n    @Override\n    public Type freshen() {\n      return make(sym, Types.freshen(typeArgs));\n    }\n\n    public static class ConstructorScheme {\n\n      private final ConstructorSymbol sym;\n      private final List<Type> typeArgs;\n      private final List<ConstructorSymbol> getterSyms;\n\n      public ConstructorScheme(\n          ConstructorSymbol sym, List<Type> typeArgs, List<ConstructorSymbol> getterSyms) {\n        this.sym = sym;\n        this.typeArgs = typeArgs;\n        this.getterSyms = getterSyms;\n        assert sym != null;\n      }\n\n      public ConstructorSymbol getSymbol() {\n        return sym;\n      }\n\n      public List<Type> getTypeArgs() {\n        return typeArgs;\n      }\n\n      public List<ConstructorSymbol> getGetterSymbols() {\n        return getterSyms;\n      }\n\n      @Override\n      public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + ((getterSyms == null) ? 0 : getterSyms.hashCode());\n        result = prime * result + sym.hashCode();\n        result = prime * result + ((typeArgs == null) ? 0 : typeArgs.hashCode());\n        return result;\n      }\n\n      @Override\n      public boolean equals(Object obj) {\n        if (this == obj) return true;\n        if (obj == null) return false;\n        if (getClass() != obj.getClass()) return false;\n        ConstructorScheme other = (ConstructorScheme) obj;\n        if (getterSyms == null) {\n          if (other.getterSyms != null) return false;\n        } else if (!getterSyms.equals(other.getterSyms)) return false;\n        if (!sym.equals(other.sym)) return false;\n        if (typeArgs == null) {\n          return other.typeArgs == null;\n        } else return typeArgs.equals(other.typeArgs);\n      }\n    }\n  }\n\n  public static class OpaqueType implements Type {\n\n    private static final AtomicInteger cnt = new AtomicInteger();\n    private final int id;\n\n    private OpaqueType() {\n      id = cnt.getAndIncrement();\n    }\n\n    public static OpaqueType get() {\n      return new OpaqueType();\n    }\n\n    @Override\n    public <I, O> O accept(TypeVisitor<I, O> visitor, I in) {\n      return visitor.visit(this, in);\n    }\n\n    @Override\n    public Type applySubst(Map<TypeVar, ? extends Type> subst) {\n      return this;\n    }\n\n    @Override\n    public String toString() {\n      return \"Opaque\" + id;\n    }\n\n    @Override\n    public Type freshen() {\n      return get();\n    }\n  }\n\n  public static class TypeIndex implements Type {\n\n    private final int index;\n\n    private TypeIndex(int index) {\n      this.index = index;\n    }\n\n    public static TypeIndex make(int index) {\n      return new TypeIndex(index);\n    }\n\n    @Override\n    public <I, O> O accept(TypeVisitor<I, O> visitor, I in) {\n      return visitor.visit(this, in);\n    }\n\n    @Override\n    public Type applySubst(Map<TypeVar, ? extends Type> subst) {\n      return this;\n    }\n\n    @Override\n    public Type freshen() {\n      return this;\n    }\n\n    @Override\n    public int hashCode() {\n      final int prime = 31;\n      int result = 1;\n      result = prime * result + index;\n      return result;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n      if (this == obj) return true;\n      if (obj == null) return false;\n      if (getClass() != obj.getClass()) return false;\n      TypeIndex other = (TypeIndex) obj;\n      return index == other.index;\n    }\n\n    @Override\n    public String toString() {\n      return \"[\" + index + \"]\";\n    }\n\n    @Override\n    public boolean isIndex() {\n      return true;\n    }\n\n    public int getIndex() {\n      return index;\n    }\n\n    public List<TypeIndex> expandAsFpIndex() {\n      switch (index) {\n        case 16:\n          return Arrays.asList(make(5), make(11));\n        case 32:\n          return Arrays.asList(make(8), make(24));\n        case 64:\n          return Arrays.asList(make(11), make(53));\n        case 128:\n          return Arrays.asList(make(15), make(113));\n        default:\n          throw new IllegalArgumentException(\"Illegal floating point width alias: \" + index);\n      }\n    }\n  }\n\n  // Helpers /////////////////////////////////////////////////////////////////\n\n  public static Set<TypeVar> getTypeVars(Type t) {\n    return getTypeVars(Collections.singletonList(t));\n  }\n\n  public static Set<TypeVar> getTypeVars(Collection<Type> t) {\n    Set<TypeVar> set = new HashSet<>();\n    getTypeVars(t, set);\n    return set;\n  }\n\n  private static void getTypeVars(Type t, Set<TypeVar> acc) {\n    t.accept(\n        new TypeVisitor<Void, Void>() {\n\n          @Override\n          public Void visit(TypeVar typeVar, Void in) {\n            acc.add(typeVar);\n            return null;\n          }\n\n          @Override\n          public Void visit(OpaqueType opaqueType, Void in) {\n            return null;\n          }\n\n          @Override\n          public Void visit(AlgebraicDataType namedType, Void in) {\n            getTypeVars(namedType.getTypeArgs(), acc);\n            return null;\n          }\n\n          @Override\n          public Void visit(TypeIndex typeIndex, Void in) {\n            return null;\n          }\n        },\n        null);\n  }\n\n  private static void getTypeVars(Collection<Type> types, Set<TypeVar> acc) {\n    for (Type t : types) {\n      getTypeVars(t, acc);\n    }\n  }\n\n  public static boolean containsTypeVarOrOpaqueType(Type t) {\n    return t.accept(\n        new TypeVisitor<Void, Boolean>() {\n\n          @Override\n          public Boolean visit(TypeVar typeVar, Void in) {\n            return true;\n          }\n\n          @Override\n          public Boolean visit(AlgebraicDataType algebraicType, Void in) {\n            for (Type ty : algebraicType.getTypeArgs()) {\n              if (ty.accept(this, in)) {\n                return true;\n              }\n            }\n            return false;\n          }\n\n          @Override\n          public Boolean visit(OpaqueType opaqueType, Void in) {\n            return true;\n          }\n\n          @Override\n          public Boolean visit(TypeIndex typeIndex, Void in) {\n            return false;\n          }\n        },\n        null);\n  }\n\n  public static boolean isSmtRepresentable(Type t) {\n    return !t.isIndex()\n        && t.accept(\n            new TypeVisitor<Void, Boolean>() {\n\n              @Override\n              public Boolean visit(TypeVar typeVar, Void in) {\n                return true;\n              }\n\n              @Override\n              public Boolean visit(AlgebraicDataType algebraicType, Void in) {\n                TypeSymbol sym = algebraicType.getSymbol();\n                if (sym instanceof BuiltInTypeSymbol) {\n                  switch ((BuiltInTypeSymbol) sym) {\n                    case ARRAY_TYPE:\n                    case BOOL_TYPE:\n                    case BV:\n                    case CMP_TYPE:\n                    case FP:\n                    case INT_TYPE:\n                    case LIST_TYPE:\n                    case OPTION_TYPE:\n                    case SMT_TYPE:\n                    case STRING_TYPE:\n                    case SYM_TYPE:\n                      break;\n                    case OPAQUE_SET:\n                    case MODEL_TYPE:\n                    case SMT_PATTERN_TYPE:\n                    case SMT_WRAPPED_VAR_TYPE:\n                      return false;\n                    default:\n                      throw new AssertionError(\"impossible\");\n                  }\n                }\n                for (Type typeArg : algebraicType.getTypeArgs()) {\n                  if (!typeArg.accept(this, in)) {\n                    return false;\n                  }\n                }\n                return true;\n              }\n\n              @Override\n              public Boolean visit(OpaqueType opaqueType, Void in) {\n                return false;\n              }\n\n              @Override\n              public Boolean visit(TypeIndex typeIndex, Void in) {\n                return true;\n              }\n            },\n            null);\n  }\n\n  public static boolean mayBePreSmtType(Type t) {\n    return !t.isIndex()\n        && t.accept(\n            new TypeVisitor<Void, Boolean>() {\n\n              @Override\n              public Boolean visit(TypeVar typeVar, Void in) {\n                return true;\n              }\n\n              @Override\n              public Boolean visit(AlgebraicDataType algebraicType, Void in) {\n                TypeSymbol sym = algebraicType.getSymbol();\n                if (sym instanceof BuiltInTypeSymbol) {\n                  switch ((BuiltInTypeSymbol) sym) {\n                    case ARRAY_TYPE:\n                    case BOOL_TYPE:\n                    case BV:\n                    case CMP_TYPE:\n                    case FP:\n                    case INT_TYPE:\n                    case LIST_TYPE:\n                    case OPTION_TYPE:\n                    case STRING_TYPE:\n                      break;\n                    case OPAQUE_SET:\n                    case MODEL_TYPE:\n                    case SMT_PATTERN_TYPE:\n                    case SMT_TYPE:\n                    case SMT_WRAPPED_VAR_TYPE:\n                    case SYM_TYPE:\n                      return false;\n                    default:\n                      throw new AssertionError(\"impossible\");\n                  }\n                }\n                for (Type typeArg : algebraicType.getTypeArgs()) {\n                  if (!typeArg.accept(this, in)) {\n                    return false;\n                  }\n                }\n                return true;\n              }\n\n              @Override\n              public Boolean visit(OpaqueType opaqueType, Void in) {\n                return false;\n              }\n\n              @Override\n              public Boolean visit(TypeIndex typeIndex, Void in) {\n                return true;\n              }\n            },\n            null);\n  }\n\n  public static boolean isTupleType(Type t) {\n    if (!(t instanceof AlgebraicDataType)) {\n      return false;\n    }\n    TypeSymbol sym = ((AlgebraicDataType) t).getSymbol();\n    return sym.equals(GlobalSymbolManager.lookupTupleTypeSymbol(sym.getArity()));\n  }\n\n  public static boolean isSmtVarType(Type t) {\n    if (!(t instanceof AlgebraicDataType)) {\n      return false;\n    }\n    return ((AlgebraicDataType) t).getSymbol().equals(BuiltInTypeSymbol.SYM_TYPE);\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/types/WellTypedProgram.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.types;\n\nimport edu.harvard.seas.pl.formulog.ast.BasicRule;\nimport edu.harvard.seas.pl.formulog.ast.Program;\nimport edu.harvard.seas.pl.formulog.ast.UserPredicate;\n\npublic interface WellTypedProgram extends Program<UserPredicate, BasicRule> {}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/unification/EmptySubstitution.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.unification;\n\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.ast.Var;\nimport java.util.Collections;\n\npublic enum EmptySubstitution implements Substitution {\n  INSTANCE;\n\n  @Override\n  public void put(Var v, Term t) {\n    throw new UnsupportedOperationException();\n  }\n\n  @Override\n  public Term get(Var v) {\n    throw new UnsupportedOperationException();\n  }\n\n  @Override\n  public boolean containsKey(Var v) {\n    return false;\n  }\n\n  @Override\n  public Iterable<Var> iterateKeys() {\n    return Collections.emptyList();\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/unification/OverwriteSubstitution.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.unification;\n\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.ast.Var;\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class OverwriteSubstitution implements Substitution {\n\n  private final Map<Var, Term> m;\n\n  public OverwriteSubstitution() {\n    this(new HashMap<>());\n  }\n\n  private OverwriteSubstitution(Map<Var, Term> m) {\n    this.m = m;\n  }\n\n  @Override\n  public void put(Var v, Term t) {\n    m.put(v, t);\n  }\n\n  @Override\n  public Term get(Var v) {\n    assert m.containsKey(v);\n    return m.get(v);\n  }\n\n  @Override\n  public boolean containsKey(Var v) {\n    return m.containsKey(v);\n  }\n\n  @Override\n  public Iterable<Var> iterateKeys() {\n    return m.keySet();\n  }\n\n  public OverwriteSubstitution copy() {\n    return new OverwriteSubstitution(new HashMap<>(m));\n  }\n\n  @Override\n  public String toString() {\n    return m.toString();\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/unification/SimpleSubstitution.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.unification;\n\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.ast.Var;\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class SimpleSubstitution implements Substitution {\n\n  private final Map<Var, Term> map = new HashMap<>();\n\n  @Override\n  public void put(Var v, Term t) {\n    map.put(v, t);\n  }\n\n  @Override\n  public Term get(Var v) {\n    Term t = map.get(v);\n    assert t != null;\n    return t;\n  }\n\n  @Override\n  public boolean containsKey(Var v) {\n    return map.containsKey(v);\n  }\n\n  @Override\n  public Iterable<Var> iterateKeys() {\n    return map.keySet();\n  }\n\n  @Override\n  public String toString() {\n    StringBuilder sb = new StringBuilder(\"[\");\n    for (Map.Entry<Var, Term> e : map.entrySet()) {\n      sb.append(e.getKey());\n      sb.append(\" -> \");\n      sb.append(e.getValue());\n      sb.append(\" \");\n    }\n    sb.append(\"]\");\n    return sb.toString();\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/unification/Substitution.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.unification;\n\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.ast.Var;\n\npublic interface Substitution {\n\n  void put(Var v, Term t);\n\n  Term get(Var v);\n\n  static Term getIfPresent(Term t, Substitution s) {\n    if (t instanceof Var && s.containsKey((Var) t)) {\n      return s.get((Var) t);\n    }\n    return t;\n  }\n\n  boolean containsKey(Var v);\n\n  Iterable<Var> iterateKeys();\n\n  //\tSubstitution copy();\n\n  //\tint newCheckpoint();\n\n  //\tint getCheckpoint();\n\n  //\tvoid revertTo(int checkpoint);\n\n  //\tint popCheckpoint();\n\n  //\tvoid mergeCheckpoint();\n\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/unification/Unification.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.unification;\n\nimport edu.harvard.seas.pl.formulog.ast.ComplexLiteral;\nimport edu.harvard.seas.pl.formulog.ast.ComplexLiterals.ComplexLiteralExnVisitor;\nimport edu.harvard.seas.pl.formulog.ast.Constructor;\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.ast.Terms;\nimport edu.harvard.seas.pl.formulog.ast.UnificationPredicate;\nimport edu.harvard.seas.pl.formulog.ast.UserPredicate;\nimport edu.harvard.seas.pl.formulog.ast.Var;\nimport edu.harvard.seas.pl.formulog.symbols.Symbol;\nimport edu.harvard.seas.pl.formulog.validating.InvalidProgramException;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\npublic final class Unification {\n\n  private Unification() {\n    throw new AssertionError();\n  }\n\n  public static boolean canBindVars(\n      ComplexLiteral atom, Set<Var> boundVars, Map<Var, Integer> varCounts)\n      throws InvalidProgramException {\n    return atom.accept(\n        new ComplexLiteralExnVisitor<Void, Boolean, InvalidProgramException>() {\n\n          @Override\n          public Boolean visit(UserPredicate normalAtom, Void in) throws InvalidProgramException {\n            return handleNormalAtom(normalAtom, boundVars, varCounts);\n          }\n\n          @Override\n          public Boolean visit(UnificationPredicate unifyAtom, Void in)\n              throws InvalidProgramException {\n            return handleUnifyAtom(unifyAtom, boundVars);\n          }\n        },\n        null);\n  }\n\n  private static boolean handleNormalAtom(\n      UserPredicate atom, Set<Var> boundVars, Map<Var, Integer> varCounts) {\n    if (atom.isNegated()) {\n      // Allow anonymous variables in negated atoms.\n      Set<Var> vars =\n          atom.varSet().stream()\n              .filter(v -> !Integer.valueOf(1).equals(varCounts.get(v)))\n              .collect(Collectors.toSet());\n      return boundVars.containsAll(vars);\n    }\n    Set<Var> nonFuncVars = new HashSet<>();\n    Set<Var> funcVars = new HashSet<>();\n    for (Term t : atom.getArgs()) {\n      nonFuncVars.addAll(Terms.getBindingVarInstances(t));\n      funcVars.addAll(Terms.getNonBindingVarInstances(t));\n    }\n    for (Var v : funcVars) {\n      if (!boundVars.contains(v) && !nonFuncVars.contains(v)) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  private static boolean handleUnifyAtom(UnificationPredicate atom, Set<Var> boundVars)\n      throws InvalidProgramException {\n    Term t1 = atom.getLhs();\n    Term t2 = atom.getRhs();\n    if (atom.isNegated()) {\n      return boundVars.containsAll(t1.varSet()) && boundVars.containsAll(t2.varSet());\n    }\n    Set<Var> maybeBoundVars = new HashSet<>(boundVars);\n    while (handleUnifyAtomHelper(t1, t2, maybeBoundVars)) {}\n\n    return maybeBoundVars.containsAll(atom.varSet());\n  }\n\n  private static boolean handleUnifyAtomHelper(Term t1, Term t2, Set<Var> boundVars)\n      throws InvalidProgramException {\n    boolean changed = false;\n    if (Terms.isGround(t1, boundVars)) {\n      changed |= boundVars.addAll(Terms.getBindingVarInstances(t2));\n    }\n    if (Terms.isGround(t2, boundVars)) {\n      changed |= boundVars.addAll(Terms.getBindingVarInstances(t1));\n    }\n    if (t1 instanceof Constructor && t2 instanceof Constructor) {\n      Constructor c1 = (Constructor) t1;\n      Constructor c2 = (Constructor) t2;\n      Symbol sym1 = c1.getSymbol();\n      Symbol sym2 = c2.getSymbol();\n      if (!sym1.equals(sym2)) {\n        throw new InvalidProgramException(\n            \"Cannot unify a constructor of symbol \"\n                + sym1\n                + \" with a constructor of symbol \"\n                + sym2);\n      }\n      Term[] args1 = c1.getArgs();\n      Term[] args2 = c2.getArgs();\n      assert args1.length == args2.length;\n      boolean changed2;\n      do {\n        changed2 = false;\n        for (int i = 0; i < args1.length; ++i) {\n          changed2 |= handleUnifyAtomHelper(args1[i], args2[i], boundVars);\n          changed |= changed2;\n        }\n      } while (changed2);\n    }\n    return changed;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/util/AbstractFJPTask.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.util;\n\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport java.util.concurrent.RecursiveAction;\n\n@SuppressWarnings(\"serial\")\npublic abstract class AbstractFJPTask extends RecursiveAction {\n\n  private final CountingFJP exec;\n\n  protected AbstractFJPTask(CountingFJP exec) {\n    this.exec = exec;\n  }\n\n  @Override\n  protected void compute() {\n    try {\n      doTask();\n      exec.reportTaskCompletion();\n    } catch (EvaluationException e) {\n      exec.fail(e);\n    }\n  }\n\n  public abstract void doTask() throws EvaluationException;\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/util/CompositeIterable.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.util;\n\nimport java.util.ArrayDeque;\nimport java.util.Iterator;\nimport java.util.NoSuchElementException;\nimport java.util.Queue;\n\npublic class CompositeIterable<T> implements Iterable<T> {\n\n  private final Iterable<Iterable<T>> its;\n\n  public CompositeIterable(Iterable<Iterable<T>> its) {\n    this.its = its;\n  }\n\n  @Override\n  public Iterator<T> iterator() {\n    return new CompositeIterator();\n  }\n\n  private final class CompositeIterator implements Iterator<T> {\n\n    private final Queue<Iterator<T>> iterators;\n\n    public CompositeIterator() {\n      Queue<Iterator<T>> l = new ArrayDeque<>();\n      for (Iterable<T> it : its) {\n        l.add(it.iterator());\n      }\n      this.iterators = l;\n    }\n\n    private boolean load() {\n      if (iterators.isEmpty()) {\n        return false;\n      }\n      if (iterators.peek().hasNext()) {\n        return true;\n      }\n      iterators.remove();\n      return load();\n    }\n\n    @Override\n    public synchronized boolean hasNext() {\n      return load();\n    }\n\n    @Override\n    public synchronized T next() {\n      if (!load()) {\n        throw new NoSuchElementException();\n      }\n      return iterators.peek().next();\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/util/CountingFJP.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.util;\n\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\n\npublic interface CountingFJP {\n\n  void externallyAddTask(AbstractFJPTask w);\n\n  void recursivelyAddTask(AbstractFJPTask w);\n\n  void reportTaskCompletion();\n\n  void blockUntilFinished();\n\n  default void blockUntilFinishedExn() throws EvaluationException {\n    blockUntilFinished();\n    if (hasFailed()) {\n      throw getFailureCause();\n    }\n  }\n\n  void shutdown();\n\n  void fail(EvaluationException cause);\n\n  boolean hasFailed();\n\n  EvaluationException getFailureCause();\n\n  long getStealCount();\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/util/CountingFJPImpl.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.util;\n\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport java.util.concurrent.ForkJoinPool;\nimport java.util.concurrent.ForkJoinTask;\nimport java.util.concurrent.ForkJoinWorkerThread;\nimport java.util.concurrent.RejectedExecutionException;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicInteger;\n\npublic class CountingFJPImpl implements CountingFJP {\n\n  private final ForkJoinPool exec;\n  private final AtomicInteger taskCount = new AtomicInteger();\n  private volatile EvaluationException failureCause;\n\n  public CountingFJPImpl(int parallelism) {\n    this.exec =\n        new ForkJoinPool(\n            parallelism,\n            ForkJoinPool.defaultForkJoinWorkerThreadFactory,\n            new Thread.UncaughtExceptionHandler() {\n\n              @Override\n              public void uncaughtException(Thread t, Throwable e) {\n                System.err.println(e);\n              }\n            },\n            false);\n  }\n\n  public void externallyAddTask(AbstractFJPTask w) {\n    taskCount.incrementAndGet();\n    try {\n      exec.execute(w);\n    } catch (RejectedExecutionException e) {\n\n    }\n  }\n\n  public void recursivelyAddTask(AbstractFJPTask w) {\n    taskCount.incrementAndGet();\n    if (ForkJoinTask.inForkJoinPool()) {\n      assert ((ForkJoinWorkerThread) Thread.currentThread()).getPool() == exec;\n      w.fork();\n    } else {\n      exec.execute(w);\n    }\n  }\n\n  public void reportTaskCompletion() {\n    if (taskCount.decrementAndGet() == 0) {\n      synchronized (taskCount) {\n        taskCount.notify();\n      }\n    }\n  }\n\n  public final void blockUntilFinished() {\n    synchronized (taskCount) {\n      while (taskCount.get() > 0 && !hasFailed()) {\n        try {\n          taskCount.wait();\n        } catch (InterruptedException e) {\n          // do nothing\n        }\n      }\n    }\n  }\n\n  public final void shutdown() {\n    exec.shutdown();\n    while (!exec.isTerminated()) {\n      try {\n        exec.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);\n      } catch (InterruptedException e) {\n        // do nothing\n      }\n    }\n  }\n\n  public final void fail(EvaluationException cause) {\n    failureCause = cause;\n    exec.shutdownNow();\n    synchronized (taskCount) {\n      taskCount.notify();\n    }\n  }\n\n  public final boolean hasFailed() {\n    return failureCause != null;\n  }\n\n  public final EvaluationException getFailureCause() {\n    return failureCause;\n  }\n\n  @Override\n  public long getStealCount() {\n    return exec.getStealCount();\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/util/Dataset.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2020-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.util;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\npublic class Dataset {\n\n  private final Set<Datum> data = Util.concurrentSet();\n\n  public void addDataPoint(double val) {\n    data.add(new Datum(val));\n  }\n\n  public int size() {\n    return data.size();\n  }\n\n  public double computeSum() {\n    double sum = 0;\n    for (Datum d : data) {\n      sum += d.val;\n    }\n    return sum;\n  }\n\n  public double computeMean() {\n    assert size() > 0;\n    return computeSum() / size();\n  }\n\n  public double computeStdDev() {\n    double mean = computeMean();\n    double varSum = 0;\n    for (Datum d : data) {\n      double delta = d.val - mean;\n      varSum += delta * delta;\n    }\n    return Math.sqrt(varSum / (data.size() - 1));\n  }\n\n  public List<Double> computeMinMedianMax() {\n    List<Datum> points = data.stream().sorted().collect(Collectors.toList());\n    int n = size();\n    double min = points.get(0).val;\n    double max = points.get(n - 1).val;\n    int mid = n / 2;\n    double median = points.get(mid).val;\n    if (n % 2 == 0) {\n      median = (median + points.get(mid - 1).val) / 2;\n    }\n    return Arrays.asList(min, median, max);\n  }\n\n  public String getStatsString(double multiplier) {\n    if (size() == 0) {\n      return \"-\";\n    }\n    List<Double> mmm = computeMinMedianMax();\n    return String.format(\n        \"n=%d,mean=%1.1f,min=%1.1f,median=%1.1f,max=%1.1f,stddev=%1.1f\",\n        size(),\n        computeMean() * multiplier,\n        mmm.get(0) * multiplier,\n        mmm.get(1) * multiplier,\n        mmm.get(2) * multiplier,\n        computeStdDev() * multiplier);\n  }\n\n  public String getStatsString() {\n    return getStatsString(1);\n  }\n\n  private static class Datum implements Comparable<Datum> {\n\n    public final double val;\n\n    public Datum(double val) {\n      this.val = val;\n    }\n\n    @Override\n    public int compareTo(Datum o) {\n      return Double.compare(val, o.val);\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/util/DedupWorkList.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.util;\n\nimport java.util.ArrayDeque;\nimport java.util.Deque;\nimport java.util.HashSet;\nimport java.util.Set;\n\npublic class DedupWorkList<T> {\n\n  private final Set<T> seen = new HashSet<>();\n  private final Deque<T> worklist = new ArrayDeque<>();\n\n  public boolean push(T item) {\n    boolean b = seen.add(item);\n    if (b) {\n      worklist.add(item);\n    }\n    return b;\n  }\n\n  public T pop() {\n    return worklist.pop();\n  }\n\n  public boolean isEmpty() {\n    return worklist.isEmpty();\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/util/EnumerableThreadLocal.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.util;\n\nimport java.util.Set;\nimport java.util.function.Consumer;\nimport java.util.function.Supplier;\n\npublic class EnumerableThreadLocal<T> {\n\n  private final Set<T> items = Util.concurrentSet();\n  private final ThreadLocal<T> tl;\n\n  public EnumerableThreadLocal(Supplier<T> f) {\n    tl =\n        ThreadLocal.withInitial(\n            () -> {\n              var item = f.get();\n              items.add(item);\n              return item;\n            });\n  }\n\n  public T get() {\n    return tl.get();\n  }\n\n  public void forEach(Consumer<T> f) {\n    items.forEach(f);\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/util/ExceptionalFunction.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.util;\n\n@FunctionalInterface\npublic interface ExceptionalFunction<T1, T2, E extends Exception> {\n\n  T2 apply(T1 arg) throws E;\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/util/FunctorUtil.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.util;\n\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.symbols.Symbol;\nimport java.util.Arrays;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Supplier;\n\npublic final class FunctorUtil {\n\n  private FunctorUtil() {\n    throw new AssertionError();\n  }\n\n  public static String toString(Symbol sym, Term[] args) {\n    StringBuilder sb = new StringBuilder(sym.toString());\n    int arity = sym.getArity();\n    if (arity > 0) {\n      sb.append('(');\n      for (int i = 0; i < arity; ++i) {\n        sb.append(args[i]);\n        if (i < arity - 1) {\n          sb.append(\", \");\n        }\n      }\n      sb.append(')');\n    }\n    return sb.toString();\n  }\n\n  // public static class Memoizer<F extends Functor> {\n  //\n  // private final Map<Object, Object> memo = new ConcurrentHashMap<>();\n  //\n  // @SuppressWarnings(\"unchecked\")\n  // public F lookupOrCreate(Symbol sym, Term[] args, Supplier<F> constructor) {\n  // if (sym.getArity() != args.length) {\n  // throw new IllegalArgumentException(\"Symbol \" + sym + \" has arity \" +\n  // sym.getArity() + \" but args \"\n  // + Arrays.toString(args) + \" have length \" + args.length);\n  // }\n  // Map<Object, Object> m = memo;\n  // Object k = sym;\n  // for (int i = 0; i < args.length; ++i) {\n  // m = (Map<Object, Object>) Util.lookupOrCreate(m, k, () -> new\n  // ConcurrentHashMap<>());\n  // k = args[i];\n  // }\n  // return (F) Util.lookupOrCreate(m, k, () -> constructor.get());\n  // }\n  //\n  // }\n\n  public static class Memoizer<T extends Term> {\n\n    private final Map<Key, T> memo = new ConcurrentHashMap<>();\n\n    public T lookupOrCreate(Symbol sym, Term[] args, Supplier<T> constructor) {\n      if (sym.getArity() != args.length) {\n        throw new IllegalArgumentException(\n            \"Symbol \"\n                + sym\n                + \" has arity \"\n                + sym.getArity()\n                + \" but args \"\n                + Arrays.toString(args)\n                + \" have arity \"\n                + args.length);\n      }\n      Key key = new Key(sym, args);\n      T f = memo.get(key);\n      if (f == null) {\n        f = constructor.get();\n        T f2 = memo.putIfAbsent(key, f);\n        if (f2 != null) {\n          f = f2;\n        }\n      }\n      return f;\n    }\n\n    private static class Key {\n\n      private final Symbol sym;\n      private final Term[] args;\n      private final int hashCode;\n\n      public Key(Symbol sym, Term[] args) {\n        this.sym = sym;\n        this.args = args;\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + Arrays.hashCode(args);\n        result = prime * result + ((sym == null) ? 0 : sym.hashCode());\n        hashCode = result;\n      }\n\n      @Override\n      public int hashCode() {\n        return hashCode;\n      }\n\n      @Override\n      public boolean equals(Object obj) {\n        if (this == obj) return true;\n        if (obj == null) return false;\n        if (getClass() != obj.getClass()) return false;\n        Key other = (Key) obj;\n        if (hashCode != other.hashCode) {\n          return false;\n        }\n        if (sym == null) {\n          if (other.sym != null) return false;\n        } else if (!sym.equals(other.sym)) return false;\n        if (!Arrays.equals(args, other.args)) return false;\n        return true;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/util/IntArrayWrapper.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.util;\n\nimport java.util.Arrays;\n\npublic class IntArrayWrapper {\n\n  private final int[] a;\n\n  public IntArrayWrapper(int[] a) {\n    this.a = a;\n  }\n\n  public int[] getVal() {\n    return a;\n  }\n\n  @Override\n  public int hashCode() {\n    return Arrays.hashCode(a);\n  }\n\n  @Override\n  public boolean equals(Object o) {\n    if (o instanceof int[]) {\n      return Arrays.equals(a, (int[]) o);\n    }\n    return false;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/util/MockCountingFJP.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.util;\n\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport java.util.ArrayDeque;\nimport java.util.Deque;\n\npublic class MockCountingFJP implements CountingFJP {\n\n  private volatile EvaluationException failureCause;\n\n  private final Deque<AbstractFJPTask> workItems = new ArrayDeque<>();\n\n  public synchronized void externallyAddTask(AbstractFJPTask w) {\n    workItems.addLast(w);\n  }\n\n  public synchronized void recursivelyAddTask(AbstractFJPTask w) {\n    workItems.addLast(w);\n  }\n\n  public final synchronized void blockUntilFinished() {\n    while (!workItems.isEmpty() && !hasFailed()) {\n      AbstractFJPTask task = workItems.removeLast();\n      task.compute();\n    }\n  }\n\n  public final synchronized void shutdown() {}\n\n  public final synchronized void fail(EvaluationException cause) {\n    failureCause = cause;\n  }\n\n  public final synchronized boolean hasFailed() {\n    return failureCause != null;\n  }\n\n  public final synchronized EvaluationException getFailureCause() {\n    return failureCause;\n  }\n\n  @Override\n  public synchronized void reportTaskCompletion() {}\n\n  @Override\n  public long getStealCount() {\n    return 0;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/util/Pair.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.util;\n\nimport java.util.Set;\nimport java.util.function.BiFunction;\nimport java.util.stream.Collectors;\n\npublic class Pair<U, V> {\n\n  private final U fst;\n  private final V snd;\n\n  public Pair(U fst, V snd) {\n    this.fst = fst;\n    this.snd = snd;\n  }\n\n  public U fst() {\n    return fst;\n  }\n\n  public V snd() {\n    return snd;\n  }\n\n  @Override\n  public int hashCode() {\n    final int prime = 31;\n    int result = 1;\n    result = prime * result + ((fst == null) ? 0 : fst.hashCode());\n    result = prime * result + ((snd == null) ? 0 : snd.hashCode());\n    return result;\n  }\n\n  @Override\n  public boolean equals(Object obj) {\n    if (this == obj) return true;\n    if (obj == null) return false;\n    if (getClass() != obj.getClass()) return false;\n    @SuppressWarnings(\"rawtypes\")\n    Pair other = (Pair) obj;\n    if (fst == null) {\n      if (other.fst != null) return false;\n    } else if (!fst.equals(other.fst)) return false;\n    if (snd == null) {\n      if (other.snd != null) return false;\n    } else if (!snd.equals(other.snd)) return false;\n    return true;\n  }\n\n  @Override\n  public String toString() {\n    return \"<\" + fst + \", \" + snd + \">\";\n  }\n\n  public static <F, S, R> Set<R> map(Set<Pair<F, S>> s, BiFunction<F, S, R> f) {\n    return s.stream().map(p -> f.apply(p.fst(), p.snd())).collect(Collectors.toSet());\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/util/SharedLong.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.util;\n\nimport java.util.Set;\n\npublic class SharedLong {\n\n  private static class Box {\n    long val;\n  }\n\n  private final Set<Box> boxes = Util.concurrentSet();\n\n  private final ThreadLocal<Box> tl =\n      ThreadLocal.withInitial(\n          () -> {\n            var box = new Box();\n            boxes.add(box);\n            return box;\n          });\n\n  public void increment() {\n    tl.get().val++;\n  }\n\n  public void add(long delta) {\n    if (delta == 0) {\n      return;\n    }\n    tl.get().val += delta;\n  }\n\n  public long unsafeGet() {\n    long sum = 0;\n    for (var box : boxes) {\n      sum += box.val;\n    }\n    return sum;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/util/StackMap.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.util;\n\nimport java.util.ArrayDeque;\nimport java.util.Deque;\nimport java.util.Iterator;\nimport java.util.Map;\n\npublic class StackMap<K, V> {\n\n  private final Deque<Map<K, V>> stack = new ArrayDeque<>();\n\n  public synchronized void push(Map<K, V> m) {\n    stack.addFirst(m);\n  }\n\n  public synchronized Map<K, V> pop() {\n    return stack.remove();\n  }\n\n  public synchronized V get(K k) {\n    for (Iterator<Map<K, V>> it = stack.iterator(); it.hasNext(); ) {\n      Map<K, V> m = it.next();\n      V v = m.get(k);\n      if (v != null) {\n        return v;\n      }\n    }\n    return null;\n  }\n\n  public synchronized V put(K k, V v) {\n    return stack.getFirst().put(k, v);\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/util/TodoException.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.util;\n\npublic class TodoException extends RuntimeException {\n\n  private static final long serialVersionUID = -3552412612033483231L;\n\n  public TodoException() {}\n\n  public TodoException(String message) {\n    super(message);\n  }\n\n  public TodoException(Throwable cause) {\n    super(cause);\n  }\n\n  public TodoException(String message, Throwable cause) {\n    super(message, cause);\n  }\n\n  public TodoException(\n      String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {\n    super(message, cause, enableSuppression, writableStackTrace);\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/util/Triple.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.util;\n\npublic class Triple<S, T, U> {\n\n  public final S first;\n  public final T second;\n  public final U third;\n\n  public Triple(S first, T second, U third) {\n    this.first = first;\n    this.second = second;\n    this.third = third;\n  }\n\n  @Override\n  public int hashCode() {\n    final int prime = 31;\n    int result = 1;\n    result = prime * result + ((first == null) ? 0 : first.hashCode());\n    result = prime * result + ((second == null) ? 0 : second.hashCode());\n    result = prime * result + ((third == null) ? 0 : third.hashCode());\n    return result;\n  }\n\n  @Override\n  public boolean equals(Object obj) {\n    if (this == obj) return true;\n    if (obj == null) return false;\n    if (getClass() != obj.getClass()) return false;\n    @SuppressWarnings(\"rawtypes\")\n    Triple other = (Triple) obj;\n    if (first == null) {\n      if (other.first != null) return false;\n    } else if (!first.equals(other.first)) return false;\n    if (second == null) {\n      if (other.second != null) return false;\n    } else if (!second.equals(other.second)) return false;\n    if (third == null) {\n      if (other.third != null) return false;\n    } else if (!third.equals(other.third)) return false;\n    return true;\n  }\n\n  @Override\n  public String toString() {\n    return \"< \" + first + \" , \" + second + \" , \" + third + \" >\";\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/util/UnionFind.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.util;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\n\npublic class UnionFind<T> {\n\n  private final Map<T, T> m = new HashMap<>();\n\n  public boolean contains(T t) {\n    return m.containsKey(t);\n  }\n\n  public boolean add(T t) {\n    return m.putIfAbsent(t, t) == null;\n  }\n\n  public void union(T t1, T t2) {\n    t1 = find(t1);\n    t2 = find(t2);\n    m.put(t1, t2);\n  }\n\n  public T find(T t) {\n    assert contains(t);\n    T prev = null;\n    while (!t.equals(prev)) {\n      prev = t;\n      t = m.get(prev);\n    }\n    return t;\n  }\n\n  public Set<T> members() {\n    return m.keySet();\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/util/Util.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.util;\n\nimport edu.harvard.seas.pl.formulog.ast.UserPredicate;\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.Future;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\nimport java.util.stream.Collectors;\nimport org.jgrapht.Graph;\n\npublic final class Util {\n\n  private Util() {\n    throw new AssertionError();\n  }\n\n  public static <K, V> V lookupOrCreate(Map<K, V> m, K k, Supplier<V> cnstr) {\n    V v = m.get(k);\n    if (v == null) {\n      v = cnstr.get();\n      V u = m.putIfAbsent(k, v);\n      if (u != null) {\n        v = u;\n      }\n    }\n    return v;\n  }\n\n  public static <T> Iterable<T> i2i(Iterator<T> it) {\n    return () -> it;\n  }\n\n  public static <K> Set<K> concurrentSet() {\n    return Collections.newSetFromMap(new ConcurrentHashMap<>());\n  }\n\n  public static <T> List<T> iterableToList(Iterable<T> it) {\n    List<T> l = new ArrayList<>();\n    it.forEach(l::add);\n    return l;\n  }\n\n  public static <A, B> List<B> map(List<A> xs, Function<A, B> f) {\n    return xs.stream().map(f).collect(Collectors.toList());\n  }\n\n  public static void printSortedFacts(Iterable<UserPredicate> facts, PrintStream out) {\n    Util.iterableToList(facts).stream()\n        .map(\n            a -> {\n              ByteArrayOutputStream baos = new ByteArrayOutputStream();\n              PrintStream ps = new PrintStream(baos);\n              ps.print(a);\n              return baos.toString();\n            })\n        .sorted()\n        .forEach(out::println);\n  }\n\n  public static <K, V> Map<K, V> fillMapWithFutures(Map<K, Future<V>> futures, Map<K, V> m)\n      throws InterruptedException, ExecutionException {\n    for (Map.Entry<K, Future<V>> e : futures.entrySet()) {\n      m.put(e.getKey(), e.getValue().get());\n    }\n    return m;\n  }\n\n  public static class IterableOfIterables<T> implements Iterable<Iterable<T>> {\n\n    private final Iterable<T> iterable;\n    private final int size;\n\n    public IterableOfIterables(Iterable<T> iterable, int size) {\n      this.iterable = iterable;\n      this.size = size;\n    }\n\n    @Override\n    public Iterator<Iterable<T>> iterator() {\n      Iterator<T> it = iterable.iterator();\n      return new Iterator<Iterable<T>>() {\n\n        @Override\n        public boolean hasNext() {\n          return it.hasNext();\n        }\n\n        @Override\n        public Iterable<T> next() {\n          assert hasNext();\n          List<T> l = new ArrayList<>(size);\n          for (int i = 0; i < size && it.hasNext(); ++i) {\n            l.add(it.next());\n          }\n          return l;\n        }\n      };\n    }\n\n    @Override\n    public String toString() {\n      String s = \"{\";\n      for (Iterator<Iterable<T>> it = iterator(); it.hasNext(); ) {\n        s += it.next();\n        if (it.hasNext()) {\n          s += \",\";\n        }\n      }\n      return s + \"}\";\n    }\n\n    public String toString(Function<T, String> printer) {\n      String s = \"{\";\n      for (Iterator<Iterable<T>> it = iterator(); it.hasNext(); ) {\n        s += \" {\";\n        for (Iterator<T> it2 = it.next().iterator(); it2.hasNext(); ) {\n          s += printer.apply(it2.next());\n          if (it2.hasNext()) {\n            s += \", \";\n          }\n        }\n        s += \"} \";\n        if (it.hasNext()) {\n          s += \", \";\n        }\n      }\n      return s + \"}\";\n    }\n  }\n\n  public static <T> Iterable<Iterable<T>> splitIterable(Iterable<T> iterable, int segmentSize) {\n    return new IterableOfIterables<>(iterable, segmentSize);\n  }\n\n  public static void clean(File f, boolean deleteTopLevel) {\n    if (!f.exists()) {\n      return;\n    }\n    if (f.isDirectory()) {\n      for (File ff : f.listFiles()) {\n        clean(ff, true);\n      }\n    }\n    if (deleteTopLevel) {\n      f.delete();\n    }\n  }\n\n  public static void assertBinaryOnPath(String exec) {\n    String os = System.getProperty(\"os.name\");\n    String util = os.startsWith(\"Windows\") ? \"where\" : \"which\";\n    String[] cmd = {util, exec};\n    String strCmd = util + \" \" + exec;\n    try {\n      Process p = Runtime.getRuntime().exec(cmd);\n      if (p.waitFor() != 0) {\n        throw new AssertionError(\n            \"Cannot find \"\n                + exec\n                + \" executable on path (`\"\n                + strCmd\n                + \"` returned a non-zero exit code).\");\n      }\n    } catch (IOException | InterruptedException e) {\n      throw new AssertionError(\n          \"Command checking for presence of \"\n              + exec\n              + \" executable failed: \"\n              + strCmd\n              + \"\\n\"\n              + e.getMessage());\n    }\n  }\n\n  public static <V, E> void printGraph(PrintStream out, Graph<V, E> g) {\n    out.println(\"{\");\n    for (Iterator<V> it = g.vertexSet().iterator(); it.hasNext(); ) {\n      V v = it.next();\n      out.println(\"\\t\" + v + \":\");\n      if (g.outDegreeOf(v) == 0) {\n        out.println(\"\\t\\tNONE\");\n      } else {\n        for (E e : g.outgoingEdgesOf(v)) {\n          out.println(\"\\t\\t\" + e);\n        }\n      }\n      if (it.hasNext()) {\n        out.println();\n      }\n    }\n    out.println(\"}\");\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/util/sexp/SExp.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.util.sexp;\n\nimport java.util.List;\n\npublic interface SExp {\n\n  boolean isAtom();\n\n  default boolean isList() {\n    return !isAtom();\n  }\n\n  String asAtom();\n\n  List<SExp> asList();\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/util/sexp/SExpAtom.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.util.sexp;\n\nimport java.util.List;\n\npublic class SExpAtom implements SExp {\n\n  private final String value;\n\n  public SExpAtom(String value) {\n    this.value = value;\n  }\n\n  @Override\n  public boolean isAtom() {\n    return true;\n  }\n\n  @Override\n  public String asAtom() {\n    return value;\n  }\n\n  @Override\n  public List<SExp> asList() {\n    throw new UnsupportedOperationException();\n  }\n\n  @Override\n  public String toString() {\n    return value;\n  }\n\n  @Override\n  public int hashCode() {\n    final int prime = 31;\n    int result = 1;\n    result = prime * result + ((value == null) ? 0 : value.hashCode());\n    return result;\n  }\n\n  @Override\n  public boolean equals(Object obj) {\n    if (this == obj) return true;\n    if (obj == null) return false;\n    if (getClass() != obj.getClass()) return false;\n    SExpAtom other = (SExpAtom) obj;\n    if (value == null) {\n      if (other.value != null) return false;\n    } else if (!value.equals(other.value)) return false;\n    return true;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/util/sexp/SExpException.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.util.sexp;\n\npublic class SExpException extends Exception {\n\n  private static final long serialVersionUID = -8286351759750678592L;\n\n  public SExpException() {}\n\n  public SExpException(String message) {\n    super(message);\n  }\n\n  public SExpException(Throwable cause) {\n    super(cause);\n  }\n\n  public SExpException(String message, Throwable cause) {\n    super(message, cause);\n  }\n\n  public SExpException(\n      String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {\n    super(message, cause, enableSuppression, writableStackTrace);\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/util/sexp/SExpLexer.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2021-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.util.sexp;\n\nimport java.io.Reader;\nimport java.io.StringReader;\n\npublic class SExpLexer {\n\n  private static final int BUF_CAPACITY = 1024;\n  private char[] buf = new char[BUF_CAPACITY];\n  private int pos = 0;\n  private int bufSize = 0;\n\n  private boolean ready;\n  private SExpToken curToken;\n  private String curValue;\n\n  private final Reader reader;\n\n  public SExpLexer(Reader reader) {\n    this.reader = reader;\n  }\n\n  public SExpToken curToken() throws SExpException {\n    if (!ready && !loadNextToken()) {\n      throw new SExpException(\"No more tokens\");\n    }\n    return curToken;\n  }\n\n  public String curValue() throws SExpException {\n    if (!ready && !loadNextToken()) {\n      throw new SExpException(\"No more tokens\");\n    }\n    if (curValue == null) {\n      throw new SExpException(\"No value associated with current token\");\n    }\n    return curValue;\n  }\n\n  public void step() throws SExpException {\n    if (!ready) {\n      loadNextToken();\n    }\n    ready = false;\n  }\n\n  public boolean hasToken() throws SExpException {\n    return ready || loadNextToken();\n  }\n\n  public void consume(SExpToken token) throws SExpException {\n    SExpToken got = curToken();\n    if (got == token) {\n      step();\n    } else {\n      throw new SExpException(\"Expected \" + token + \" but got \" + got);\n    }\n  }\n\n  private boolean loadNextToken() throws SExpException {\n    curToken = null;\n    curValue = null;\n    while (load() && Character.isWhitespace(buf[pos])) {\n      pos++;\n    }\n    if (!load()) {\n      return false;\n    }\n    char ch = buf[pos];\n    switch (ch) {\n      case '(':\n        curToken = SExpToken.LPAREN;\n        pos++;\n        break;\n      case ')':\n        curToken = SExpToken.RPAREN;\n        pos++;\n        break;\n      default:\n        curToken = SExpToken.ATOM;\n        curValue = readAtom();\n    }\n    ready = true;\n    return true;\n  }\n\n  private String readAtom() throws SExpException {\n    StringBuilder sb = new StringBuilder();\n    char ch;\n    while (load() && (ch = buf[pos]) != ')' && ch != '(' && !Character.isWhitespace(ch)) {\n      sb.append(ch);\n      pos++;\n    }\n    return sb.toString();\n  }\n\n  private boolean load() throws SExpException {\n    if (pos >= bufSize) {\n      try {\n        bufSize = reader.read(buf);\n      } catch (Exception e) {\n        throw new SExpException(e);\n      }\n      pos = 0;\n    }\n    return bufSize > 0;\n  }\n\n  public static void main(String[] args) throws SExpException {\n    Reader r = new StringReader(\"(hello 2(x ()) )\");\n    SExpLexer lexer = new SExpLexer(r);\n    while (lexer.hasToken()) {\n      switch (lexer.curToken) {\n        case ATOM:\n          System.out.println(lexer.curValue());\n          break;\n        case LPAREN:\n          System.out.println(\"(\");\n          break;\n        case RPAREN:\n          System.out.println(\")\");\n          break;\n      }\n      lexer.step();\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/util/sexp/SExpList.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.util.sexp;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Iterator;\nimport java.util.List;\n\npublic class SExpList implements SExp {\n\n  private final List<SExp> l;\n\n  public SExpList(List<SExp> l) {\n    this.l = Collections.unmodifiableList(new ArrayList<>(l));\n  }\n\n  @Override\n  public boolean isAtom() {\n    return false;\n  }\n\n  @Override\n  public String asAtom() {\n    throw new UnsupportedOperationException();\n  }\n\n  @Override\n  public List<SExp> asList() {\n    return l;\n  }\n\n  @Override\n  public String toString() {\n    StringBuilder sb = new StringBuilder();\n    sb.append(\"(\");\n    for (Iterator<SExp> it = l.iterator(); it.hasNext(); ) {\n      sb.append(it.next());\n      if (it.hasNext()) {\n        sb.append(\" \");\n      }\n    }\n    sb.append(\")\");\n    return sb.toString();\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/util/sexp/SExpParser.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2021-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.util.sexp;\n\nimport java.io.Reader;\nimport java.io.StringReader;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class SExpParser {\n\n  private final SExpLexer lexer;\n\n  public SExpParser(Reader r) {\n    lexer = new SExpLexer(r);\n  }\n\n  public SExp parse() throws SExpException {\n    switch (lexer.curToken()) {\n      case ATOM:\n        SExp atom = new SExpAtom(lexer.curValue());\n        lexer.step();\n        return atom;\n      case LPAREN:\n        return parseList();\n      case RPAREN:\n        throw new SExpException(\"Unexpected right parenthesis\");\n      default:\n        throw new AssertionError(\"Impossible\");\n    }\n  }\n\n  private SExp parseList() throws SExpException {\n    lexer.consume(SExpToken.LPAREN);\n    List<SExp> l = new ArrayList<>();\n    boolean go = true;\n    while (go) {\n      switch (lexer.curToken()) {\n        case ATOM:\n          l.add(new SExpAtom(lexer.curValue()));\n          lexer.step();\n          break;\n        case LPAREN:\n          l.add(parseList());\n          break;\n        case RPAREN:\n          go = false;\n          break;\n      }\n    }\n    lexer.consume(SExpToken.RPAREN);\n    return new SExpList(l);\n  }\n\n  public static void main(String[] args) throws SExpException {\n    Reader r = new StringReader(\"(hello 2(x ()) )\");\n    SExpParser parser = new SExpParser(r);\n    System.out.println(parser.parse());\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/util/sexp/SExpToken.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.util.sexp;\n\npublic enum SExpToken {\n  LPAREN,\n  RPAREN,\n  ATOM;\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/validating/FunctionDefValidation.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.validating;\n\nimport edu.harvard.seas.pl.formulog.ast.Program;\nimport edu.harvard.seas.pl.formulog.ast.Var;\nimport edu.harvard.seas.pl.formulog.functions.FunctionDef;\nimport edu.harvard.seas.pl.formulog.functions.UserFunctionDef;\nimport edu.harvard.seas.pl.formulog.symbols.FunctionSymbol;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Set;\n\npublic class FunctionDefValidation {\n\n  private FunctionDefValidation() {\n    throw new AssertionError();\n  }\n\n  public static void validate(Program<?, ?> prog) throws InvalidProgramException {\n    for (FunctionSymbol sym : prog.getFunctionSymbols()) {\n      FunctionDef def = prog.getDef(sym);\n      if (def instanceof UserFunctionDef) {\n        try {\n          validate((UserFunctionDef) def);\n        } catch (InvalidProgramException e) {\n          throw new InvalidProgramException(\n              \"Invalid function definition: \" + sym + \"\\n\" + e.getMessage());\n        }\n      }\n    }\n  }\n\n  public static void validate(UserFunctionDef def) throws InvalidProgramException {\n    Set<Var> params = checkParams(def.getParams());\n    Set<Var> vars = def.getBody().varSet();\n    vars.removeAll(params);\n    if (!vars.isEmpty()) {\n      String msg = \"Unbound variable(s): \";\n      for (Iterator<Var> it = vars.iterator(); it.hasNext(); ) {\n        msg += it.next();\n        if (it.hasNext()) {\n          msg += \", \";\n        }\n      }\n      throw new InvalidProgramException(msg);\n    }\n  }\n\n  private static Set<Var> checkParams(List<Var> params) throws InvalidProgramException {\n    Set<Var> vars = new HashSet<>();\n    for (Var param : params) {\n      if (!vars.add(param)) {\n        throw new InvalidProgramException(\"Repeated parameter: \" + param);\n      }\n    }\n    return vars;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/validating/InvalidProgramException.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.validating;\n\npublic class InvalidProgramException extends Exception {\n\n  private static final long serialVersionUID = 5958219607857975431L;\n\n  /** Constructs an exception signifying an ill-formedness error. */\n  public InvalidProgramException() {}\n\n  /**\n   * Constructs an exception signifying an ill-formedness error.\n   *\n   * @param message the error message\n   */\n  public InvalidProgramException(String message) {\n    super(message);\n  }\n\n  /**\n   * Constructs an exception signifying an ill-formedness error.\n   *\n   * @param cause the exception that caused this exception\n   */\n  public InvalidProgramException(Throwable cause) {\n    super(cause);\n  }\n\n  /**\n   * Constructs an exception signifying an ill-formedness error.\n   *\n   * @param message the error message\n   * @param cause the exception that caused this exception\n   */\n  public InvalidProgramException(String message, Throwable cause) {\n    super(message, cause);\n  }\n\n  /**\n   * Constructs an exception signifying an ill-formedness error.\n   *\n   * @param message the error message\n   * @param cause the exception that caused this exception\n   * @param enableSuppression whether or not suppression is enabled or disabled\n   * @param writableStackTrace whether or not the stack trace should be writable\n   */\n  public InvalidProgramException(\n      String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {\n    super(message, cause, enableSuppression, writableStackTrace);\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/validating/Stratifier.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.validating;\n\nimport edu.harvard.seas.pl.formulog.Configuration;\nimport edu.harvard.seas.pl.formulog.ast.BasicRule;\nimport edu.harvard.seas.pl.formulog.ast.ComplexLiteral;\nimport edu.harvard.seas.pl.formulog.ast.ComplexLiterals.ComplexLiteralVisitor;\nimport edu.harvard.seas.pl.formulog.ast.Constructor;\nimport edu.harvard.seas.pl.formulog.ast.Expr;\nimport edu.harvard.seas.pl.formulog.ast.Exprs.ExprVisitor;\nimport edu.harvard.seas.pl.formulog.ast.Fold;\nimport edu.harvard.seas.pl.formulog.ast.FunctionCallFactory.FunctionCall;\nimport edu.harvard.seas.pl.formulog.ast.LetFunExpr;\nimport edu.harvard.seas.pl.formulog.ast.MatchClause;\nimport edu.harvard.seas.pl.formulog.ast.MatchExpr;\nimport edu.harvard.seas.pl.formulog.ast.Primitive;\nimport edu.harvard.seas.pl.formulog.ast.Program;\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.ast.Terms.TermVisitor;\nimport edu.harvard.seas.pl.formulog.ast.UnificationPredicate;\nimport edu.harvard.seas.pl.formulog.ast.UserPredicate;\nimport edu.harvard.seas.pl.formulog.ast.Var;\nimport edu.harvard.seas.pl.formulog.functions.FunctionDef;\nimport edu.harvard.seas.pl.formulog.functions.UserFunctionDef;\nimport edu.harvard.seas.pl.formulog.symbols.BuiltInFunctionSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.FunctionSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.PredicateFunctionSymbol;\nimport edu.harvard.seas.pl.formulog.symbols.RelationSymbol;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.stream.Collectors;\nimport org.jgrapht.Graph;\nimport org.jgrapht.alg.connectivity.KosarajuStrongConnectivityInspector;\nimport org.jgrapht.alg.interfaces.StrongConnectivityAlgorithm;\nimport org.jgrapht.graph.DefaultDirectedGraph;\nimport org.jgrapht.graph.DefaultEdge;\nimport org.jgrapht.traverse.TopologicalOrderIterator;\n\npublic class Stratifier {\n\n  private final Program<UserPredicate, BasicRule> prog;\n\n  public Stratifier(Program<UserPredicate, BasicRule> prog) {\n    this.prog = prog;\n  }\n\n  public List<Stratum> stratify() throws InvalidProgramException {\n    Graph<RelationSymbol, DependencyTypeWrapper> g =\n        new DefaultDirectedGraph<>(DependencyTypeWrapper.class);\n    for (RelationSymbol sym : prog.getRuleSymbols()) {\n      DependencyFinder depends = new DependencyFinder();\n      for (BasicRule r : prog.getRules(sym)) {\n        for (ComplexLiteral bd : r) {\n          depends.processAtom(bd);\n        }\n        UserPredicate hd = r.getHead();\n        for (Term t : hd.getArgs()) {\n          depends.processTerm(t);\n        }\n      }\n      g.addVertex(sym);\n      for (RelationSymbol bdSym : depends) {\n        g.addVertex(bdSym);\n        g.addEdge(bdSym, sym, new DependencyTypeWrapper(depends.getDependencyType(bdSym)));\n      }\n    }\n    StrongConnectivityAlgorithm<RelationSymbol, DependencyTypeWrapper> k =\n        new KosarajuStrongConnectivityInspector<>(g);\n    Graph<Graph<RelationSymbol, DependencyTypeWrapper>, DefaultEdge> condensation =\n        k.getCondensation();\n    TopologicalOrderIterator<Graph<RelationSymbol, DependencyTypeWrapper>, DefaultEdge> topo =\n        new TopologicalOrderIterator<>(condensation);\n    List<Stratum> strata = new ArrayList<>();\n    int rank = 0;\n    InvalidProgramException exn = null;\n    while (topo.hasNext()) {\n      boolean hasRecursiveNegationOrAggregation = false;\n      Graph<RelationSymbol, DependencyTypeWrapper> component = topo.next();\n      Set<RelationSymbol> edbs =\n          component.vertexSet().stream().filter(r -> r.isEdbSymbol()).collect(Collectors.toSet());\n      if (!edbs.isEmpty()) {\n        if (!component.edgeSet().isEmpty()) {\n          throw new InvalidProgramException(\"EDB relations cannot have dependencies: \" + edbs);\n        }\n        continue;\n      }\n      for (DependencyTypeWrapper dw : component.edgeSet()) {\n        DependencyType d = dw.get();\n        if (d.equals(DependencyType.NEG_OR_AGG_IN_FUN) && exn == null) {\n          exn =\n              new InvalidProgramException(\n                  \"Not stratified: the relation \"\n                      + component.getEdgeSource(dw)\n                      + \" is treated as an aggregate expression in the definition of relation \"\n                      + component.getEdgeTarget(dw));\n        }\n        hasRecursiveNegationOrAggregation |= d.equals(DependencyType.NEG_OR_AGG_IN_REL);\n      }\n      strata.add(new Stratum(rank, component.vertexSet(), hasRecursiveNegationOrAggregation));\n      if (Configuration.debugStratification) {\n        toDot(rank, component);\n      }\n      rank++;\n    }\n    if (exn != null) {\n      throw exn;\n    }\n    return strata;\n  }\n\n  private static void toDot(\n      int stratumNumber, Graph<RelationSymbol, DependencyTypeWrapper> component) {\n    try {\n      Path base = Files.createDirectories(Paths.get(Configuration.debugStratificationOutDir));\n      String name = \"stratum_\" + stratumNumber;\n      File out = base.resolve(name + \".dot\").toFile();\n      try (PrintWriter pw = new PrintWriter(out)) {\n        pw.println(\"digraph \" + name + \" {\");\n        for (RelationSymbol sym : component.vertexSet()) {\n          pw.println(\"\\t\" + sym + \";\");\n        }\n        for (DependencyTypeWrapper e : component.edgeSet()) {\n          pw.print(\"\\t\");\n          pw.print(component.getEdgeSource(e));\n          pw.print(\" -> \");\n          pw.print(component.getEdgeTarget(e));\n          pw.println(\" [label=\" + e + \"];\");\n        }\n        pw.println(\"}\");\n      }\n    } catch (IOException e) {\n      System.err.println(\"Error while writing stratification debug info\");\n    }\n  }\n\n  private class DependencyFinder implements Iterable<RelationSymbol> {\n\n    private final Set<FunctionSymbol> visitedFunctions = new HashSet<>();\n    private final Set<RelationSymbol> allDependencies = new HashSet<>();\n    private final Set<RelationSymbol> negOrAggFunDependencies = new HashSet<>();\n    private final Set<RelationSymbol> negOrAggRelDependencies = new HashSet<>();\n\n    // private boolean isAggregate(Atom a) {\n    // Symbol sym = a.getSymbol();\n    // RelationProperties props = prog.getRelationProperties(sym);\n    // return props != null && props.isAggregated();\n    // }\n\n    public void processAtom(ComplexLiteral a) {\n      a.accept(\n          new ComplexLiteralVisitor<Void, Void>() {\n\n            @Override\n            public Void visit(UnificationPredicate unificationPredicate, Void input) {\n              processTerm(unificationPredicate.getLhs());\n              processTerm(unificationPredicate.getRhs());\n              return null;\n            }\n\n            @Override\n            public Void visit(UserPredicate userPredicate, Void input) {\n              if (userPredicate.isNegated()) {\n                addNegOrAggRel(userPredicate.getSymbol());\n              } else {\n                addPositive(userPredicate.getSymbol());\n              }\n              for (Term t : userPredicate.getArgs()) {\n                processTerm(t);\n              }\n              return null;\n            }\n          },\n          null);\n    }\n\n    public DependencyType getDependencyType(RelationSymbol sym) {\n      // Order is important here, since having a negative or aggregate\n      // dependency within a function body subsumes having one in a\n      // relation definition.\n      if (negOrAggFunDependencies.contains(sym)) {\n        return DependencyType.NEG_OR_AGG_IN_FUN;\n      }\n      if (negOrAggRelDependencies.contains(sym)) {\n        return DependencyType.NEG_OR_AGG_IN_REL;\n      }\n      return DependencyType.POSITIVE;\n    }\n\n    private void addNegOrAggFun(RelationSymbol sym) {\n      negOrAggFunDependencies.add(sym);\n      allDependencies.add(sym);\n    }\n\n    private void addNegOrAggRel(RelationSymbol sym) {\n      negOrAggRelDependencies.add(sym);\n      allDependencies.add(sym);\n    }\n\n    private void addPositive(RelationSymbol sym) {\n      allDependencies.add(sym);\n    }\n\n    public void processTerm(Term t) {\n      t.accept(\n          new TermVisitor<Void, Void>() {\n\n            @Override\n            public Void visit(Var t, Void in) {\n              return null;\n            }\n\n            @Override\n            public Void visit(Constructor t, Void in) {\n              for (Term arg : t.getArgs()) {\n                arg.accept(this, null);\n              }\n              return null;\n            }\n\n            @Override\n            public Void visit(Primitive<?> prim, Void in) {\n              return null;\n            }\n\n            @Override\n            public Void visit(Expr expr, Void in) {\n              processExpr(expr);\n              return null;\n            }\n          },\n          null);\n    }\n\n    private void processExpr(Expr expr) {\n      expr.accept(\n          new ExprVisitor<Void, Void>() {\n\n            @Override\n            public Void visit(MatchExpr matchExpr, Void in) {\n              processTerm(matchExpr.getMatchee());\n              for (MatchClause cl : matchExpr) {\n                processTerm(cl.getLhs());\n                processTerm(cl.getRhs());\n              }\n              return null;\n            }\n\n            @Override\n            public Void visit(FunctionCall funcCall, Void in) {\n              processFunctionSymbol(funcCall.getSymbol());\n              for (Term arg : funcCall.getArgs()) {\n                processTerm(arg);\n              }\n              return null;\n            }\n\n            @Override\n            public Void visit(LetFunExpr funcDef, Void in) {\n              throw new AssertionError(\"impossible\");\n            }\n\n            @Override\n            public Void visit(Fold fold, Void in) {\n              fold.getShamCall().accept(this, in);\n              return null;\n            }\n          },\n          null);\n    }\n\n    private void processFunctionSymbol(FunctionSymbol sym) {\n      if (!visitedFunctions.add(sym) || sym instanceof BuiltInFunctionSymbol) {\n        return;\n      }\n      if (sym instanceof PredicateFunctionSymbol) {\n        addNegOrAggFun(((PredicateFunctionSymbol) sym).getPredicateSymbol());\n        return;\n      }\n      FunctionDef def1 = prog.getDef(sym);\n      if (def1 instanceof UserFunctionDef) {\n        UserFunctionDef def = (UserFunctionDef) def1;\n        processTerm(def.getBody());\n      }\n    }\n\n    @Override\n    public Iterator<RelationSymbol> iterator() {\n      return allDependencies.iterator();\n    }\n  }\n\n  private static enum DependencyType {\n    NEG_OR_AGG_IN_FUN,\n\n    NEG_OR_AGG_IN_REL,\n\n    POSITIVE;\n  }\n\n  // Needed because edges need to have unique objects as labels...\n  private static class DependencyTypeWrapper {\n\n    private final DependencyType d;\n\n    public DependencyTypeWrapper(DependencyType d) {\n      this.d = d;\n    }\n\n    public DependencyType get() {\n      return d;\n    }\n\n    @Override\n    public String toString() {\n      return d.toString();\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/validating/Stratum.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.validating;\n\nimport edu.harvard.seas.pl.formulog.symbols.RelationSymbol;\nimport java.util.Set;\n\npublic class Stratum {\n\n  private final int rank;\n  private final Set<RelationSymbol> predicateSyms;\n  private final boolean hasRecursiveNegationOrAggregation;\n\n  public Stratum(\n      int rank, Set<RelationSymbol> predicateSyms, boolean hasRecursiveNegationOrAggregation) {\n    this.rank = rank;\n    this.predicateSyms = predicateSyms;\n    this.hasRecursiveNegationOrAggregation = hasRecursiveNegationOrAggregation;\n  }\n\n  public int getRank() {\n    return rank;\n  }\n\n  public Set<RelationSymbol> getPredicateSyms() {\n    return predicateSyms;\n  }\n\n  public boolean hasRecursiveNegationOrAggregation() {\n    return hasRecursiveNegationOrAggregation;\n  }\n\n  @Override\n  public String toString() {\n    return \"Stratum [rank=\"\n        + rank\n        + \", predicateSyms=\"\n        + predicateSyms\n        + \", hasRecursiveNegationOrAggregation=\"\n        + hasRecursiveNegationOrAggregation\n        + \"]\";\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/validating/ValidRule.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.validating;\n\nimport edu.harvard.seas.pl.formulog.ast.AbstractRule;\nimport edu.harvard.seas.pl.formulog.ast.ComplexLiteral;\nimport edu.harvard.seas.pl.formulog.ast.Rule;\nimport edu.harvard.seas.pl.formulog.ast.UserPredicate;\nimport edu.harvard.seas.pl.formulog.ast.Var;\nimport edu.harvard.seas.pl.formulog.unification.Unification;\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.BiFunction;\n\npublic class ValidRule extends AbstractRule<UserPredicate, ComplexLiteral> {\n\n  public static ValidRule make(\n      Rule<UserPredicate, ComplexLiteral> rule, BiFunction<ComplexLiteral, Set<Var>, Integer> score)\n      throws InvalidProgramException {\n    try {\n      List<ComplexLiteral> body = Util.iterableToList(rule);\n      Set<Var> vars = new HashSet<>();\n      // Reordering is currently unsound... also need to type check.\n      order(body, score, vars, rule.countVariables());\n      // Set<Var> vars = checkBody(rule);\n      UserPredicate head = rule.getHead();\n      if (!head.getSymbol().isIdbSymbol()) {\n        throw new InvalidProgramException(\n            \"Cannot create a rule for non-IDB symbol \" + head.getSymbol());\n      }\n      if (!vars.containsAll(head.varSet())) {\n        String msg = \"There are unbound variables in the head of a rule:\";\n        for (Var x : head.varSet()) {\n          if (!vars.contains(x)) {\n            msg += \" \" + x;\n          }\n        }\n        throw new InvalidProgramException(msg);\n      }\n      return new ValidRule(head, body);\n    } catch (InvalidProgramException e) {\n      throw new InvalidProgramException(e.getMessage() + \"\\n\" + rule);\n    }\n  }\n\n  public static void order(\n      List<ComplexLiteral> atoms,\n      BiFunction<ComplexLiteral, Set<Var>, Integer> score,\n      Set<Var> boundVars,\n      Map<Var, Integer> varCounts)\n      throws InvalidProgramException {\n    List<ComplexLiteral> newList = new ArrayList<>();\n    // Using a linked hash set ensures the sort is stable.\n    Set<ComplexLiteral> unplaced = new LinkedHashSet<>(atoms);\n    while (!unplaced.isEmpty()) {\n      ComplexLiteral bestLit = null;\n      int bestScore = -1;\n      for (ComplexLiteral l : unplaced) {\n        if (Unification.canBindVars(l, boundVars, varCounts)) {\n          int localScore = score.apply(l, boundVars);\n          if (localScore > bestScore) {\n            bestScore = localScore;\n            bestLit = l;\n          }\n        }\n      }\n      if (bestLit == null) {\n        throw new InvalidProgramException(\"Literals do not admit an evaluable reordering\");\n      }\n      newList.add(bestLit);\n      boundVars.addAll(bestLit.varSet());\n      unplaced.remove(bestLit);\n    }\n    atoms.clear();\n    atoms.addAll(newList);\n  }\n\n  // private static Set<Var> checkBody(Rule<UserPredicate, ComplexLiteral> rule)\n  //     throws InvalidProgramException {\n  //   Set<Var> boundVars = new HashSet<>();\n  //   Map<Var, Integer> varCounts = rule.countVariables();\n  //   for (ComplexLiteral lit : rule) {\n  //     if (!Unification.canBindVars(lit, boundVars, varCounts)) {\n  //       throw new InvalidProgramException(\n  //           \"Rule cannot be evaluated given the supplied order.\\n\"\n  //               + \"The problematic rule is:\\n\"\n  //               + rule\n  //               + \"\\nThe problematic literal is: \"\n  //               + lit);\n  //     }\n  //     boundVars.addAll(lit.varSet());\n  //   }\n  //   return boundVars;\n  // }\n\n  private ValidRule(UserPredicate head, List<ComplexLiteral> body) {\n    super(head, body);\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/validating/ast/Assignment.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.validating.ast;\n\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.ast.Var;\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport edu.harvard.seas.pl.formulog.unification.Substitution;\nimport java.util.Set;\n\npublic class Assignment implements SimpleLiteral {\n\n  private final Var var;\n  private final Term rhs;\n\n  public static Assignment make(Var var, Term rhs) {\n    return new Assignment(var, rhs);\n  }\n\n  public Assignment(Var var, Term rhs) {\n    this.var = var;\n    this.rhs = rhs;\n  }\n\n  public void assign(Substitution subst) throws EvaluationException {\n    subst.put(var, rhs.normalize(subst));\n  }\n\n  @Override\n  public <I, O> O accept(SimpleLiteralVisitor<I, O> visitor, I input) {\n    return visitor.visit(this, input);\n  }\n\n  @Override\n  public <I, O, E extends Throwable> O accept(SimpleLiteralExnVisitor<I, O, E> visitor, I input)\n      throws E {\n    return visitor.visit(this, input);\n  }\n\n  @Override\n  public String toString() {\n    return var + \" <- \" + rhs;\n  }\n\n  @Override\n  public void varSet(Set<Var> vars) {\n    vars.add(var);\n    rhs.varSet(vars);\n  }\n\n  @Override\n  public SimpleLiteralTag getTag() {\n    return SimpleLiteralTag.ASSIGNMENT;\n  }\n\n  @Override\n  public Term[] getArgs() {\n    return new Term[] {var, rhs};\n  }\n\n  public Var getDef() {\n    return var;\n  }\n\n  public Term getVal() {\n    return rhs;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/validating/ast/Check.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.validating.ast;\n\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.ast.Var;\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport edu.harvard.seas.pl.formulog.unification.Substitution;\nimport java.util.Set;\n\npublic class Check implements SimpleLiteral {\n\n  private final Term lhs;\n  private final Term rhs;\n  private final boolean negated;\n\n  public static Check make(Term lhs, Term rhs, boolean negated) {\n    return new Check(lhs, rhs, negated);\n  }\n\n  private Check(Term lhs, Term rhs, boolean negated) {\n    this.lhs = lhs;\n    this.rhs = rhs;\n    this.negated = negated;\n  }\n\n  public boolean isNegated() {\n    return negated;\n  }\n\n  public Term getLhs() {\n    return lhs;\n  }\n\n  public Term getRhs() {\n    return rhs;\n  }\n\n  public boolean check(Substitution subst) throws EvaluationException {\n    Term lhs = this.lhs.normalize(subst);\n    Term rhs = this.rhs.normalize(subst);\n    assert lhs.isGround();\n    assert !lhs.containsUnevaluatedTerm();\n    assert rhs.isGround();\n    assert !rhs.containsUnevaluatedTerm();\n    return lhs.equals(rhs) ^ negated;\n  }\n\n  @Override\n  public <I, O> O accept(SimpleLiteralVisitor<I, O> visitor, I input) {\n    return visitor.visit(this, input);\n  }\n\n  @Override\n  public <I, O, E extends Throwable> O accept(SimpleLiteralExnVisitor<I, O, E> visitor, I input)\n      throws E {\n    return visitor.visit(this, input);\n  }\n\n  @Override\n  public String toString() {\n    return lhs + (negated ? \" != \" : \" = \") + rhs;\n  }\n\n  @Override\n  public void varSet(Set<Var> vars) {\n    lhs.varSet(vars);\n    rhs.varSet(vars);\n  }\n\n  @Override\n  public SimpleLiteralTag getTag() {\n    return SimpleLiteralTag.CHECK;\n  }\n\n  @Override\n  public Term[] getArgs() {\n    return new Term[] {lhs, rhs};\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/validating/ast/Destructor.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.validating.ast;\n\nimport edu.harvard.seas.pl.formulog.ast.Constructor;\nimport edu.harvard.seas.pl.formulog.ast.Constructors;\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.ast.Var;\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport edu.harvard.seas.pl.formulog.symbols.ConstructorSymbol;\nimport edu.harvard.seas.pl.formulog.unification.Substitution;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.Set;\n\npublic class Destructor implements SimpleLiteral {\n\n  private final Term x;\n  private final ConstructorSymbol symbol;\n  private final Var[] bindings;\n\n  public static Destructor make(Term x, ConstructorSymbol symbol, Var[] bindings) {\n    assert assertDisjoint(x.varSet(), Arrays.asList(bindings))\n        : \"A variable cannot be on both sides of a destructor\";\n    assert symbol.getArity() == bindings.length : \"Symbol arity does not match number of variables\";\n    assert bindings.length == new HashSet<>(Arrays.asList(bindings)).size()\n        : \"All variables must be distinct\";\n    return new Destructor(x, symbol, bindings);\n  }\n\n  private static boolean assertDisjoint(Collection<Var> xs, Collection<Var> ys) {\n    for (Var x : xs) {\n      if (ys.contains(x)) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  private Destructor(Term x, ConstructorSymbol symbol, Var[] vars) {\n    this.x = x;\n    this.symbol = symbol;\n    this.bindings = vars;\n  }\n\n  public Term getScrutinee() {\n    return x;\n  }\n\n  public ConstructorSymbol getSymbol() {\n    return symbol;\n  }\n\n  public Var[] getBindings() {\n    return bindings;\n  }\n\n  public boolean destruct(Substitution subst) throws EvaluationException {\n    var scrutinee = x.normalize(subst);\n    if (!(scrutinee instanceof Constructor)) {\n      return false;\n    }\n    Constructor ctor = (Constructor) scrutinee;\n    if (!ctor.getSymbol().equals(symbol)) {\n      return false;\n    }\n    Term[] args = ctor.getArgs();\n    for (int i = 0; i < args.length; ++i) {\n      subst.put(bindings[i], args[i]);\n    }\n    return true;\n  }\n\n  @Override\n  public <I, O> O accept(SimpleLiteralVisitor<I, O> visitor, I input) {\n    return visitor.visit(this, input);\n  }\n\n  @Override\n  public <I, O, E extends Throwable> O accept(SimpleLiteralExnVisitor<I, O, E> visitor, I input)\n      throws E {\n    return visitor.visit(this, input);\n  }\n\n  @Override\n  public String toString() {\n    String s = x + \" -> \" + symbol + \"(\";\n    for (int i = 0; i < bindings.length; ++i) {\n      s += bindings[i];\n      if (i < bindings.length - 1) {\n        s += \", \";\n      }\n    }\n    s += \")\";\n    return s;\n  }\n\n  @Override\n  public void varSet(Set<Var> vars) {\n    x.varSet(vars);\n    vars.addAll(Arrays.asList(bindings));\n  }\n\n  @Override\n  public SimpleLiteralTag getTag() {\n    return SimpleLiteralTag.DESTRUCTOR;\n  }\n\n  @Override\n  public Term[] getArgs() {\n    Term[] args = new Term[bindings.length];\n    for (int i = 0; i < args.length; ++i) {\n      args[i] = bindings[i];\n    }\n    return new Term[] {x, Constructors.make(symbol, args)};\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/validating/ast/SimpleLiteral.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.validating.ast;\n\nimport edu.harvard.seas.pl.formulog.ast.Literal;\nimport edu.harvard.seas.pl.formulog.ast.Var;\nimport java.util.HashSet;\nimport java.util.Set;\n\npublic interface SimpleLiteral extends Literal {\n\n  <I, O> O accept(SimpleLiteralVisitor<I, O> visitor, I input);\n\n  <I, O, E extends Throwable> O accept(SimpleLiteralExnVisitor<I, O, E> visitor, I input) throws E;\n\n  default Set<Var> varSet() {\n    Set<Var> vars = new HashSet<>();\n    varSet(vars);\n    return vars;\n  }\n\n  void varSet(Set<Var> vars);\n\n  SimpleLiteralTag getTag();\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/validating/ast/SimpleLiteralExnVisitor.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.validating.ast;\n\npublic interface SimpleLiteralExnVisitor<I, O, E extends Throwable> {\n\n  O visit(Assignment assignment, I input) throws E;\n\n  O visit(Check check, I input) throws E;\n\n  O visit(Destructor destructor, I input) throws E;\n\n  O visit(SimplePredicate predicate, I input) throws E;\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/validating/ast/SimpleLiteralTag.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.validating.ast;\n\npublic enum SimpleLiteralTag {\n  ASSIGNMENT,\n  CHECK,\n  DESTRUCTOR,\n  PREDICATE;\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/validating/ast/SimpleLiteralVisitor.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.validating.ast;\n\npublic interface SimpleLiteralVisitor<I, O> {\n\n  O visit(Assignment assignment, I input);\n\n  O visit(Check check, I input);\n\n  O visit(Destructor destructor, I input);\n\n  O visit(SimplePredicate predicate, I input);\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/validating/ast/SimplePredicate.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.validating.ast;\n\nimport edu.harvard.seas.pl.formulog.ast.BindingType;\nimport edu.harvard.seas.pl.formulog.ast.Term;\nimport edu.harvard.seas.pl.formulog.ast.Var;\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport edu.harvard.seas.pl.formulog.symbols.RelationSymbol;\nimport edu.harvard.seas.pl.formulog.unification.Substitution;\nimport java.util.Arrays;\nimport java.util.Set;\n\npublic class SimplePredicate implements SimpleLiteral {\n\n  private final RelationSymbol symbol;\n  private final Term[] args;\n  private final BindingType[] bindingPattern;\n  private final boolean negated;\n\n  public static SimplePredicate make(\n      RelationSymbol symbol, Term[] args, BindingType[] bindingPattern, boolean negated) {\n    assert symbol.getArity() == args.length : \"Symbol does not match argument arity\";\n    return new SimplePredicate(symbol, args, bindingPattern, negated);\n  }\n\n  private SimplePredicate(\n      RelationSymbol symbol, Term[] args, BindingType[] bindingPattern, boolean negated) {\n    this.symbol = symbol;\n    this.args = args;\n    this.bindingPattern = bindingPattern;\n    this.negated = negated;\n  }\n\n  public RelationSymbol getSymbol() {\n    return symbol;\n  }\n\n  @Override\n  public Term[] getArgs() {\n    return args;\n  }\n\n  public BindingType[] getBindingPattern() {\n    return bindingPattern;\n  }\n\n  public boolean isNegated() {\n    return negated;\n  }\n\n  @Override\n  public <I, O> O accept(SimpleLiteralVisitor<I, O> visitor, I input) {\n    return visitor.visit(this, input);\n  }\n\n  @Override\n  public <I, O, E extends Throwable> O accept(SimpleLiteralExnVisitor<I, O, E> visitor, I input)\n      throws E {\n    return visitor.visit(this, input);\n  }\n\n  @Override\n  public String toString() {\n    String s = \"\";\n    if (negated) {\n      s += \"!\";\n    }\n    s += symbol;\n    if (args.length > 0) {\n      s += \"(\";\n      for (int i = 0; i < args.length; ++i) {\n        s += args[i] + \" : \";\n        switch (bindingPattern[i]) {\n          case BOUND:\n            s += \"b\";\n            break;\n          case FREE:\n            s += \"f\";\n            break;\n          case IGNORED:\n            s += \"i\";\n            break;\n        }\n        if (i < args.length - 1) {\n          s += \", \";\n        }\n      }\n      s += \")\";\n    }\n    return s;\n  }\n\n  public SimplePredicate normalize(Substitution s) throws EvaluationException {\n    Term[] newArgs = new Term[args.length];\n    for (int i = 0; i < args.length; ++i) {\n      newArgs[i] = args[i].normalize(s);\n    }\n    return new SimplePredicate(symbol, newArgs, bindingPattern, negated);\n  }\n\n  @Override\n  public int hashCode() {\n    final int prime = 31;\n    int result = 1;\n    result = prime * result + Arrays.hashCode(args);\n    result = prime * result + Arrays.hashCode(bindingPattern);\n    result = prime * result + (negated ? 1231 : 1237);\n    result = prime * result + ((symbol == null) ? 0 : symbol.hashCode());\n    return result;\n  }\n\n  @Override\n  public boolean equals(Object obj) {\n    if (this == obj) return true;\n    if (obj == null) return false;\n    if (getClass() != obj.getClass()) return false;\n    SimplePredicate other = (SimplePredicate) obj;\n    if (!Arrays.equals(args, other.args)) return false;\n    if (!Arrays.equals(bindingPattern, other.bindingPattern)) return false;\n    if (negated != other.negated) return false;\n    if (symbol == null) {\n      if (other.symbol != null) return false;\n    } else if (!symbol.equals(other.symbol)) return false;\n    return true;\n  }\n\n  @Override\n  public void varSet(Set<Var> vars) {\n    for (Term arg : args) {\n      arg.varSet(vars);\n    }\n  }\n\n  @Override\n  public SimpleLiteralTag getTag() {\n    return SimpleLiteralTag.PREDICATE;\n  }\n}\n"
  },
  {
    "path": "src/main/java/edu/harvard/seas/pl/formulog/validating/ast/SimpleRule.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.validating.ast;\n\nimport edu.harvard.seas.pl.formulog.Configuration;\nimport edu.harvard.seas.pl.formulog.ast.*;\nimport edu.harvard.seas.pl.formulog.ast.ComplexLiterals.ComplexLiteralExnVisitor;\nimport edu.harvard.seas.pl.formulog.unification.SimpleSubstitution;\nimport edu.harvard.seas.pl.formulog.unification.Substitution;\nimport edu.harvard.seas.pl.formulog.validating.InvalidProgramException;\nimport edu.harvard.seas.pl.formulog.validating.ValidRule;\nimport java.util.*;\n\npublic class SimpleRule extends AbstractRule<SimplePredicate, SimpleLiteral> {\n\n  public static SimpleRule make(ValidRule rule, FunctionCallFactory funcFactory)\n      throws InvalidProgramException {\n    Map<Var, Integer> varCounts = rule.countVariables();\n    Simplifier simplifier = new Simplifier(varCounts);\n    for (ComplexLiteral atom : rule) {\n      try {\n        simplifier.add(atom);\n      } catch (InvalidProgramException e) {\n        throw new InvalidProgramException(\n            \"Problem simplifying this rule:\\n\"\n                + rule\n                + \"\\nCould not simplify this atom: \"\n                + atom\n                + \"\\nReason:\\n\"\n                + e.getMessage());\n      }\n    }\n    UserPredicate head = rule.getHead().applySubstitution(simplifier.getSubst());\n    Set<Var> boundVars = simplifier.getBoundVars();\n    if (!boundVars.containsAll(head.varSet())) {\n      throw new InvalidProgramException(\"Unbound variables in head of rule:\\n\" + rule);\n    }\n    Term[] headArgs = head.getArgs();\n    BindingType[] pat = computeBindingPattern(headArgs, boundVars, varCounts);\n    SimplePredicate newHead =\n        SimplePredicate.make(head.getSymbol(), head.getArgs(), pat, head.isNegated());\n    return new SimpleRule(newHead, simplifier.getConjuncts());\n  }\n\n  // XXX This isn't great because it doesn't check to make sure the invariants of\n  // a SimpleRule are actually maintained.\n  //\tpublic static SimpleRule make(SimplePredicate head, List<SimpleLiteral> body) {\n  //\t\treturn new SimpleRule(head, body);\n  //\t}\n\n  private SimpleRule(SimplePredicate head, List<SimpleLiteral> body) {\n    super(head, body);\n  }\n\n  private static BindingType[] computeBindingPattern(\n      Term[] args, Set<Var> boundVars, Map<Var, Integer> counts) {\n    BindingType[] pat = new BindingType[args.length];\n    for (int i = 0; i < pat.length; ++i) {\n      Term arg = args[i];\n      if (arg instanceof Var && Integer.valueOf(1).equals(counts.get(arg))) {\n        pat[i] = BindingType.IGNORED;\n      } else if (boundVars.containsAll(arg.varSet())) {\n        pat[i] = BindingType.BOUND;\n      } else {\n        pat[i] = BindingType.FREE;\n      }\n    }\n    return pat;\n  }\n\n  private static class Simplifier {\n\n    private final List<SimpleLiteral> acc = new ArrayList<>();\n    private final Set<Var> boundVars = new HashSet<>();\n    private final Map<Var, Integer> varCounts;\n    private final Substitution subst = new SimpleSubstitution();\n\n    public Simplifier(Map<Var, Integer> varCounts) {\n      this.varCounts = varCounts;\n    }\n\n    public Substitution getSubst() {\n      return subst;\n    }\n\n    public void add(ComplexLiteral atom) throws InvalidProgramException {\n      List<ComplexLiteral> todo = new ArrayList<>();\n      atom = atom.applySubstitution(subst);\n      SimpleLiteral c =\n          atom.accept(\n              new ComplexLiteralExnVisitor<Void, SimpleLiteral, InvalidProgramException>() {\n\n                @Override\n                public SimpleLiteral visit(UnificationPredicate unificationPredicate, Void input)\n                    throws InvalidProgramException {\n                  Term lhs = unificationPredicate.getLhs();\n                  Term rhs = unificationPredicate.getRhs();\n                  boolean leftBound = boundVars.containsAll(lhs.varSet());\n                  boolean rightBound = boundVars.containsAll(rhs.varSet());\n                  if (unificationPredicate.isNegated() && !(leftBound && rightBound)) {\n                    throw new InvalidProgramException();\n                  }\n                  if (leftBound && rightBound) {\n                    return Check.make(lhs, rhs, unificationPredicate.isNegated());\n                  } else if (rightBound) {\n                    if (lhs instanceof Var) {\n                      if (Configuration.inlineInRules || rhs instanceof Var) {\n                        subst.put((Var) lhs, rhs);\n                        return null;\n                      }\n                      return Assignment.make((Var) lhs, rhs);\n                    }\n                    if (!(lhs instanceof Constructor)) {\n                      throw new InvalidProgramException();\n                    }\n                    return makeDestructor(rhs, (Constructor) lhs, todo);\n                  } else if (leftBound) {\n                    if (rhs instanceof Var) {\n                      if (Configuration.inlineInRules || lhs instanceof Var) {\n                        subst.put((Var) rhs, lhs);\n                        return null;\n                      }\n                      return Assignment.make((Var) rhs, lhs);\n                    }\n                    if (!(rhs instanceof Constructor)) {\n                      throw new InvalidProgramException();\n                    }\n                    return makeDestructor(lhs, (Constructor) rhs, todo);\n                  } else {\n                    if (!(lhs instanceof Constructor) || !(rhs instanceof Constructor)) {\n                      throw new InvalidProgramException();\n                    }\n                    Constructor c1 = (Constructor) lhs;\n                    Constructor c2 = (Constructor) rhs;\n                    if (!c1.getSymbol().equals(c2.getSymbol())) {\n                      throw new InvalidProgramException(\"Unsatisfiable unification conjunct\");\n                    }\n                    List<ComplexLiteral> cs = new ArrayList<>();\n                    Term[] args1 = c1.getArgs();\n                    Term[] args2 = c2.getArgs();\n                    for (int i = 0; i < args1.length; ++i) {\n                      cs.add(UnificationPredicate.make(args1[i], args2[i], false));\n                    }\n                    // XXX Not reordering because of type soundness issues.\n                    // ValidRule.order(cs, (p, xs) -> 1, new HashSet<>(boundVars), varCounts);\n                    for (ComplexLiteral c : cs) {\n                      todo.add(c);\n                    }\n                    return null;\n                  }\n                }\n\n                private Destructor makeDestructor(\n                    Term boundTerm, Constructor unboundCtor, List<ComplexLiteral> todo) {\n                  Term[] args = unboundCtor.getArgs();\n                  Var[] vars = new Var[args.length];\n                  for (int i = 0; i < args.length; ++i) {\n                    Var y = Var.fresh();\n                    vars[i] = y;\n                    todo.add(UnificationPredicate.make(y, args[i], false));\n                  }\n                  return Destructor.make(boundTerm, unboundCtor.getSymbol(), vars);\n                }\n\n                @Override\n                public SimpleLiteral visit(UserPredicate userPredicate, Void input) {\n                  Term[] args = userPredicate.getArgs();\n                  Term[] newArgs = new Term[args.length];\n                  Set<Var> seen = new HashSet<>();\n                  for (int i = 0; i < args.length; ++i) {\n                    Term arg = args[i];\n                    if (boundVars.containsAll(arg.varSet())) {\n                      newArgs[i] = arg;\n                    } else if (arg instanceof Var && seen.add((Var) arg)) {\n                      newArgs[i] = arg;\n                    } else {\n                      Var y = Var.fresh();\n                      newArgs[i] = y;\n                      todo.add(UnificationPredicate.make(y, arg, false));\n                    }\n                  }\n                  BindingType[] pat = computeBindingPattern(newArgs, boundVars, varCounts);\n                  SimpleLiteral c =\n                      SimplePredicate.make(\n                          userPredicate.getSymbol(), newArgs, pat, userPredicate.isNegated());\n                  return c;\n                }\n              },\n              null);\n      if (c != null) {\n        acc.add(c);\n        boundVars.addAll(c.varSet());\n      }\n      for (ComplexLiteral x : todo) {\n        add(x);\n      }\n    }\n\n    public List<SimpleLiteral> getConjuncts() {\n      return acc;\n    }\n\n    public Set<Var> getBoundVars() {\n      return boundVars;\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/codegen/.gitignore",
    "content": "cmake-build-*/\n"
  },
  {
    "path": "src/main/resources/codegen/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.21)\nproject(codegen)\n\nset(CMAKE_CXX_STANDARD 17)\n\nset(CMAKE_CXX_FLAGS \"-D__EMBEDDED_SOUFFLE__ -DRAM_DOMAIN_SIZE=64 -Wall -Wno-unused-variable -Wno-unused-but-set-variable -Wno-c++11-narrowing\")\nset(CMAKE_CXX_FLAGS_RELEASE \"${CMAKE_CXX_FLAGS_RELEASE} -O3 -march=native\")\n\nadd_executable(flg \"\")\n\nset(SOUFFLE_FLAGS -w -j 2 -PRamSIPS:strict)\n\noption(FLG_EAGER_EVAL \"Generate code performing eager evaluation (requires custom Soufflé)\" OFF)\nif (FLG_EAGER_EVAL)\n    set(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -DFLG_EAGER_EVAL\")\n    set(SOUFFLE_FLAGS ${SOUFFLE_FLAGS} --eager-eval)\nendif ()\n\noption(FLG_RECORD_WORK \"Report amount of work performed during evaluation (requires custom Soufflé)\" OFF)\nif (FLG_RECORD_WORK)\n    set(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -DFLG_RECORD_WORK\")\n    set(SOUFFLE_FLAGS ${SOUFFLE_FLAGS} --record-work)\nendif ()\n\nadd_custom_command(OUTPUT formulog.cpp\n        COMMAND souffle ${CMAKE_CURRENT_SOURCE_DIR}/src/formulog.dl ${SOUFFLE_FLAGS} -g formulog.cpp\n        DEPENDS src/formulog.dl)\n\ntarget_sources(flg PRIVATE\n        src/time.hpp\n        src/ConcurrentHashMap.hpp\n        src/funcs.hpp\n        src/functors.cpp\n        src/functors.h\n        src/main.cpp\n        src/parser.cpp\n        src/parser.hpp\n        src/Symbol.cpp\n        src/Symbol.hpp\n        src/Term.cpp\n        src/Term.hpp\n        src/Type.hpp\n        src/Type.cpp\n        src/globals.h\n        src/set.cpp\n        src/set.hpp\n        src/smt_solver.cpp\n        src/smt_solver.h\n        src/smt_shim.cpp\n        src/smt_shim.h\n        src/smt_parser.cpp\n        src/smt_parser.hpp\n        formulog.cpp\n        )\n\nfind_package(Boost 1.81 REQUIRED COMPONENTS filesystem system program_options)\nfind_package(TBB REQUIRED)\ntarget_link_libraries(flg PRIVATE ${Boost_LIBRARIES} TBB::tbb)\n\nfind_package(OpenMP)\nif (OpenMP_CXX_FOUND)\n    target_link_libraries(flg PRIVATE OpenMP::OpenMP_CXX)\n    set(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -fopenmp\")\nendif ()\n\noption(FLG_DEV \"Formulog codegen development mode\" OFF)\nif (FLG_DEV)\n    set(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -DFLG_DEV\")\nendif ()\n"
  },
  {
    "path": "src/main/resources/codegen/src/ConcurrentHashMap.hpp",
    "content": "//\n// Created by Aaron Bembenek on 4/25/23.\n//\n\n#pragma once\n\n#include <oneapi/tbb/concurrent_unordered_map.h>\n\nnamespace flg {\n\ntemplate<typename Key, typename Value, typename Hash = std::hash<Key>, typename Equals = std::equal_to<Key>>\nusing ConcurrentHashMap = tbb::concurrent_unordered_map<Key, Value, Hash, Equals>;\n\n}"
  },
  {
    "path": "src/main/resources/codegen/src/Symbol.cpp",
    "content": "#include \"Symbol.hpp\"\n\nnamespace flg {\n\nostream& operator<<(ostream& out, Symbol sym) {\n  switch (sym) {\n    case Symbol::boxed_bool: return out << \"boxed_bool\";\n    case Symbol::boxed_i32: return out << \"boxed_i32\";\n    case Symbol::boxed_i64: return out << \"boxed_i64\";\n    case Symbol::boxed_fp32: return out << \"boxed_fp32\";\n    case Symbol::boxed_fp64: return out << \"boxed_fp64\";\n    case Symbol::boxed_string: return out << \"boxed_string\";\n    case Symbol::model: return out << \"model\";\n    case Symbol::opaque_set: return out << \"opaque_set\";\n/* INSERT 0 */\n  }\n  __builtin_unreachable();\n}\n\nvoid initialize_symbols() {\n/* INSERT 1 */\n}\n\nSymbol lookup_symbol(const string& name) {\n  auto it = symbol_table.find(name);\n  if (it != symbol_table.end()) {\n    return it->second;\n  }\n  cerr << \"Unrecognized symbol: \" << name << endl;\n  abort();\n}\n\nSymbol lookup_tuple_symbol(size_t arity) {\n  switch (arity) {\n/* INSERT 2 */\n    default:\n    cerr << \"Unrecognized tuple arity: \" << arity << endl;\n    abort();\n  }\n}\n\n} // namespace flg\n"
  },
  {
    "path": "src/main/resources/codegen/src/Symbol.hpp",
    "content": "#pragma once\n\n#include <cstdlib>\n#include <iostream>\n#include <map>\n#include <string>\n\nnamespace flg {\n\nusing namespace std;\n\nenum class Symbol {\n    boxed_bool,\n    boxed_i32,\n    boxed_i64,\n    boxed_fp32,\n    boxed_fp64,\n    boxed_string,\n    model,\n    opaque_set,\n#ifdef FLG_DEV\n    nil,\n    cons,\n    smt_and,\n    smt_not,\n    smt_imp,\n    smt_or,\n    smt_var__bool__bool,\n#endif\n/* INSERT 0 */\n};\n\ninline map<string, Symbol> symbol_table;\n\nostream &operator<<(ostream &out, Symbol sym);\n\nvoid initialize_symbols();\n\nSymbol lookup_symbol(const string &name);\n\nSymbol lookup_tuple_symbol(size_t arity);\n\nconstexpr size_t symbol_arity(Symbol sym) {\n    switch (sym) {\n/* INSERT 1 */\n        default:\n            abort();\n    }\n#ifdef FLG_DEV\n    return 0;\n#endif\n}\n\n} // namespace flg\n"
  },
  {
    "path": "src/main/resources/codegen/src/Term.cpp",
    "content": "#include <array>\n#include <stack>\n#include <type_traits>\n#include <utility>\n\n#include \"Term.hpp\"\n\nnamespace flg {\n\nostream &operator<<(ostream &out, const Term &t) {\n    switch (t.sym) {\n        case Symbol::boxed_bool: {\n            return out << boolalpha << t.as_base<bool>().val << noboolalpha;\n        }\n        case Symbol::boxed_i32: {\n            return out << t.as_base<int32_t>().val;\n        }\n        case Symbol::boxed_i64: {\n            return out << t.as_base<int64_t>().val << \"L\";\n        }\n        case Symbol::boxed_fp32: {\n            auto val = t.as_base<float>().val;\n            if (isnan(val)) {\n                out << \"fp32_nan\";\n            } else if (isinf(val)) {\n                if (val > 0) {\n                    out << \"fp32_pos_infinity\";\n                } else {\n                    out << \"fp32_neg_infinity\";\n                }\n            } else {\n                out << val << \"F\";\n            }\n            return out;\n        }\n        case Symbol::boxed_fp64: {\n            auto val = t.as_base<double>().val;\n            if (isnan(val)) {\n                out << \"fp64_nan\";\n            } else if (isinf(val)) {\n        if (val > 0) {\n          out << \"fp64_pos_infinity\";\n        } else {\n          out << \"fp64_neg_infinity\";\n        }\n      } else {\n        out << val << \"F\";\n      }\n      return out;\n    }\n    case Symbol::boxed_string: {\n      return out << \"\\\"\" << t.as_base<string>().val << \"\\\"\";\n    }\n    default: {\n      auto& x = t.as_complex();\n      out << x.sym;\n      size_t n = x.arity;\n      if (n > 0) {\n        out << \"(\";\n        for (size_t i = 0; i < n; ++i) {\n          out << *x.val[i];\n          if (i < n - 1) {\n            out << \", \";\n          }\n        }\n        out << \")\";\n      }\n      return out;\n    }\n  }\n}\n\n// Concurrency-safe cache for ComplexTerm values\ntemplate <Symbol S> class ComplexTermCache {\n  inline static constexpr size_t arity = symbol_arity(S);\n  typedef array<term_ptr, arity> key_t;\n  typedef ConcurrentHashMap<key_t, term_ptr, boost::hash<key_t>> map_t;\n  inline static map_t cache;\n\n  public:\n  template <typename... T,\n            typename = enable_if_t<sizeof...(T) == arity>>\n  static term_ptr get(T... val) {\n      array<term_ptr, arity> arr = {val...};\n      auto it = cache.find(arr);\n      if (it != cache.end()) {\n          return it->second;\n      }\n      auto *heap_arr = new term_ptr[arity]{val...};\n      auto ptr = new ComplexTerm(S, arity, heap_arr);\n      auto result = cache.emplace(arr, ptr);\n      if (!result.second) {\n          // Term was not successfully inserted\n          delete ptr;\n      }\n      return result.first->second;\n  }\n};\n\ntemplate<Symbol S, typename... T>\nterm_ptr Term::make(T... val) {\n    return ComplexTermCache<S>::get(val...);\n}\n\n/* INSERT 0 */\n\nterm_ptr ComplexTerm::fresh_smt_var() {\n#ifdef FLG_DEV\n    return nullptr;\n#else\n    return new ComplexTerm(Symbol::smt_var__bool__bool, 0, nullptr);\n#endif\n}\n\nterm_ptr Term::make_generic(Symbol sym, const vector<term_ptr> &terms) {\n    if (symbol_arity(sym) != terms.size()) {\n        string message = \"Expected arity \";\n        message += to_string(symbol_arity(sym));\n        message += \" for symbol (ID \";\n        message += to_string(static_cast<int>(sym));\n        message += \"), got arity \";\n        message += to_string(terms.size());\n        throw std::runtime_error(message);\n    }\n    switch (sym) {\n/* INSERT 1 */\n        default:\n            throw std::runtime_error(\n                    \"Invalid symbol (ID \" +\n                    to_string(static_cast<int>(sym)) +\n                    \") used to construct term\"\n            );\n    }\n}\n\n\n}\n"
  },
  {
    "path": "src/main/resources/codegen/src/Term.hpp",
    "content": "#pragma once\n\n#include <cassert>\n#include <cmath>\n#include <cstdint>\n#include <vector>\n\n#include <boost/container_hash/hash.hpp>\n#include <souffle/SouffleInterface.h>\n\n#include \"set.hpp\"\n#include \"ConcurrentHashMap.hpp\"\n#include \"Symbol.hpp\"\n\n#define NO_COPY_OR_ASSIGN(t) \\\n  t(const t&) = delete; t(t&&) = delete; \\\n  t& operator=(const t&) = delete; \\\n  t& operator=(t&&) = delete;\n\nnamespace flg {\n\nusing namespace std;\n\nstruct Term;\n\ntypedef const Term *term_ptr;\n\ntemplate<typename T>\nstruct BaseTerm;\nstruct ComplexTerm;\n\ntemplate<typename T, Symbol S>\nclass BaseTermCache;\n\ntemplate<Symbol S>\nclass ComplexTermCache;\n\nstruct Term {\n    const Symbol sym;\n\n    NO_COPY_OR_ASSIGN(Term);\n\n    template<typename T>\n    [[nodiscard]] inline const BaseTerm<T> &as_base() const;\n\n    [[nodiscard]] inline const ComplexTerm &as_complex() const;\n\n    // Construct a memoized BaseTerm\n    template<typename T>\n    inline static term_ptr make(T val);\n\n    template<typename T>\n    inline static term_ptr make_moved(T &&val);\n\n    // Construct a memoized ComplexTerm\n    template<Symbol S, typename... T>\n    static term_ptr make(T... val);\n\n    // Convert a Lisp-style list term into a vector\n    inline static vector<term_ptr> vectorize_list_term(term_ptr t);\n\n    [[nodiscard]] souffle::RamDomain intize() const {\n        return (souffle::RamDomain) (uintptr_t) (void *) this;\n    }\n\n    static term_ptr unintize(souffle::RamDomain id) {\n        return (term_ptr) (void *) (uintptr_t) id;\n    }\n\n    static term_ptr make_generic(Symbol sym, const vector<term_ptr> &terms);\n\nprotected:\n    explicit Term(Symbol sym_) : sym{sym_} {}\n};\n\nstruct ComplexTerm : public Term {\n    const size_t arity;\n    const term_ptr *const val;\n\n    NO_COPY_OR_ASSIGN(ComplexTerm);\n\n    static term_ptr fresh_smt_var();\n\nprivate:\n    ComplexTerm(Symbol sym_, size_t arity_, term_ptr *val_) :\n            Term{sym_}, arity{arity_}, val{val_} {}\n\n    ~ComplexTerm() {\n        delete[] val;\n    }\n\n    template<Symbol> friend\n    class ComplexTermCache;\n};\n\ntemplate<typename T>\nstruct BaseTerm : public Term {\n    const T val;\n\n    NO_COPY_OR_ASSIGN(BaseTerm);\n\nprivate:\n    BaseTerm(Symbol sym_, T &&val_) : Term{sym_}, val{std::move(val_)} {}\n\n    template<typename, Symbol> friend\n    class BaseTermCache;\n};\n\nostream &operator<<(ostream &out, const Term &t);\n\n// Concurrency-safe cache for BaseTerm values\ntemplate<typename T, Symbol S>\nclass BaseTermCache {\n    struct Hash {\n        std::size_t operator()(const T *const &val) const {\n            return boost::hash<T>{}(*val);\n        }\n    };\n\n    struct Equals {\n        bool operator()(const T *const &val1, const T *const &val2) const {\n            return *val1 == *val2;\n        }\n    };\n\n    typedef ConcurrentHashMap<const T*, term_ptr, Hash, Equals> map_t;\n    inline static map_t cache;\n\npublic:\n    static term_ptr get(T &&val) {\n        auto it = cache.find(&val);\n        if (it != cache.end()) {\n            return it->second;\n        }\n        auto ptr = new BaseTerm<T>(S, std::move(val));\n        auto result = cache.emplace(&ptr->val, ptr);\n        if (!result.second) {\n            // Term was not successfully inserted\n            delete ptr;\n        }\n        return result.first->second;\n    }\n};\n\ntemplate<>\ninline term_ptr Term::make<bool>(bool val) {\n    typedef BaseTermCache<bool, Symbol::boxed_bool> cache;\n    // Optimization to avoid unnecessary lock contention\n    static const term_ptr true_term = cache::get(true);\n    static const term_ptr false_term = cache::get(false);\n    return val ? true_term : false_term;\n}\n\ntemplate<>\ninline term_ptr Term::make<int32_t>(int32_t val) {\n    return BaseTermCache<int32_t, Symbol::boxed_i32>::get(std::move(val));\n}\n\ntemplate<>\ninline term_ptr Term::make<int64_t>(int64_t val) {\n    return BaseTermCache<int64_t, Symbol::boxed_i64>::get(std::move(val));\n}\n\ntemplate<>\ninline term_ptr Term::make<float>(float val) {\n    typedef BaseTermCache<float, Symbol::boxed_fp32> cache;\n    // NaN is a special case due to ill-behaved floating point comparison\n    static const term_ptr nan32_term = cache::get(nanf(\"\"));\n    if (isnan(val)) {\n        return nan32_term;\n    }\n    return cache::get(std::move(val));\n}\n\ntemplate<>\ninline term_ptr Term::make<double>(double val) {\n    typedef BaseTermCache<double, Symbol::boxed_fp64> cache;\n    // NaN is a special case due to ill-behaved floating point comparison\n    static const term_ptr nan64_term = cache::get(nan(\"\"));\n    if (isnan(val)) {\n        return nan64_term;\n    }\n    return cache::get(std::move(val));\n}\n\ntemplate<>\ninline term_ptr Term::make<string>(string val) {\n    return BaseTermCache<string, Symbol::boxed_string>::get(std::move(val));\n}\n\ntemplate<>\ninline term_ptr Term::make_moved<string>(string &&val) {\n    return BaseTermCache<string, Symbol::boxed_string>::get(std::move(val));\n}\n\ntypedef std::map<term_ptr, term_ptr> Model;\n\ntemplate<>\ninline term_ptr Term::make_moved<Model>(Model &&val) {\n    return BaseTermCache<Model, Symbol::model>::get(std::move(val));\n}\n\ntemplate<>\ninline term_ptr Term::make_moved(Set &&val) {\n    return BaseTermCache<Set, Symbol::opaque_set>::get(std::move(val));\n}\n\ntemplate<typename T>\nconst BaseTerm<T> &Term::as_base() const {\n    return reinterpret_cast<const BaseTerm<T> &>(*this);\n}\n\nconst ComplexTerm &Term::as_complex() const {\n    return reinterpret_cast<const ComplexTerm &>(*this);\n}\n\ninline vector<term_ptr> Term::vectorize_list_term(term_ptr t) {\n    vector<term_ptr> v;\n#ifndef FLG_DEV\n    while (t->sym == Symbol::cons) {\n      auto& x = t->as_complex();\n      v.push_back(x.val[0]);\n      t = x.val[1];\n    }\n    assert(t->sym == Symbol::nil);\n#endif\n    return v;\n}\n\n} // namespace flg\n"
  },
  {
    "path": "src/main/resources/codegen/src/Tuple.hpp",
    "content": "#pragma once\n\n#include <array>\n#include <iostream>\n#include <string>\n\nnamespace flg {\n\nusing namespace std;\n\ntemplate <size_t N>\nstruct Tuple {\n  array<term_ptr, N> val;\n\n  inline const term_ptr& operator[](int idx) const {\n    return val[idx];\n  }\n\n  inline term_ptr& operator[](int idx) {\n    return val[idx];\n  }\n\n  inline bool operator<(const Tuple<N>& other) const {\n    for (size_t i = 0; i < N; ++i) {\n      int cmp = Term::compare_natural((*this)[i], other[i]);\n      if (cmp != 0) {\n        return cmp < 0;\n      }\n    }\n    return false;\n  }\n};\n\ntemplate <size_t N>\ninline ostream& operator<<(ostream& out, const Tuple<N>& tup)\n{\n  out << \"(\";\n  for (size_t i = 0; i < N; ++i) {\n    out << *tup[i];\n    if (i < N - 1) {\n      out << \", \";\n    }\n  }\n  out << \")\";\n  return out;\n}\n\ntemplate <typename Index>\ninline void print_relation(const string& name, bool sorted, const Index& btree) {\n  vector<typename Index::element_type> v(btree.begin(), btree.end());\n  if (sorted) {\n    sort(v.begin(), v.end());\n  }\n  for (const auto& tuple : v) {\n    cout << name << tuple << '\\n';\n  }\n  cout << flush;\n}\n\n// Inspired by the comparator in souffle/CompiledIndexUtils.h\ntemplate <unsigned... Columns>\nstruct Comparator;\n\ntemplate <unsigned First, unsigned... Rest>\nstruct Comparator<First, Rest...> {\n\n  template <size_t N>\n  inline int operator()(const Tuple<N>& a, const Tuple<N>& b) const {\n    int cmp = Term::compare(a[First], b[First]);\n    return cmp ? cmp : Comparator<Rest...>()(a, b);\n  }\n\n  template <size_t N>\n  inline bool less(const Tuple<N>& a, const Tuple<N>& b) const {\n    int cmp = Term::compare(a[First], b[First]);\n    return cmp ? cmp < 0 : Comparator<Rest...>().less(a, b);\n  }\n\n  template <size_t N>\n  inline bool equal(const Tuple<N>& a, const Tuple<N>& b) const {\n    int cmp = Term::compare(a[First], b[First]);\n    return cmp ? false : Comparator<Rest...>().equal(a, b);\n  }\n};\n\ntemplate <>\nstruct Comparator<> {\n\n  template <size_t N>\n  inline int operator()(const Tuple<N>& a, const Tuple<N>& b) const {\n    return 0;\n  }\n\n  template <size_t N>\n  inline bool less(const Tuple<N>& a, const Tuple<N>& b) const {\n    return false;\n  }\n\n  template <size_t N>\n  inline bool equal(const Tuple<N>& a, const Tuple<N>& b) const {\n    return true;\n  }\n};\n\n} // namespace flg\n"
  },
  {
    "path": "src/main/resources/codegen/src/Type.cpp",
    "content": "//\n// Created by Aaron Bembenek on 8/10/22.\n//\n\n#include \"Type.hpp\"\n\nnamespace flg {\n\nvoid TypeSubst::put(const Type &var, const Type &other) {\n    assert(var.is_var);\n    if (other.is_var) {\n        if (var < other) {\n            m.emplace(var, other);\n        } else if (other < var) {\n            m.emplace(other, var);\n        }\n    } else {\n        m.emplace(var, other);\n    }\n}\n\nType TypeSubst::apply(const Type &ty) {\n    if (ty.is_var) {\n        auto v = m.find(ty);\n        if (v == m.end()) {\n            return ty;\n        } else {\n            return apply(v->second);\n        }\n    }\n    std::vector<Type> newArgs;\n    for (auto &arg: ty.args) {\n        newArgs.push_back(apply(arg));\n    }\n    return Type{ty.name, ty.is_var, newArgs};\n}\n\nvoid TypeSubst::clear() {\n    m.clear();\n}\n\nstd::ostream &operator<<(std::ostream &out, const Type &type) {\n    auto args = type.args;\n    if (!args.empty()) {\n        out << \"(\";\n    }\n    out << type.name;\n    for (auto &arg: args) {\n        out << \" \" << arg;\n    }\n    if (!args.empty()) {\n        out << \")\";\n    }\n    return out;\n}\n\nfunctor_type Type::make_prim(const std::string &name, const std::vector<Type> &args) {\n    return make_pair(std::vector<Type>(), Type{name, false, args});\n}\n\nfunctor_type Type::make_prim(const std::string &name) {\n    return make_prim(name, {});\n}\n\nType Type::make_index(const std::string &name) {\n    return Type{name, false, {}};\n}\n\nfunctor_type Type::bool_ = make_prim(\"Bool\");\nfunctor_type Type::i32 = make_prim(\"_ BitVec\", {make_index(\"32\")});\nfunctor_type Type::i64 = make_prim(\"_ BitVec\", {make_index(\"64\")});\nfunctor_type Type::fp32 = make_prim(\"_ FloatingPoint\", {make_index(\"8\"), make_index(\"24\")});\nfunctor_type Type::fp64 = make_prim(\"_ FloatingPoint\", {make_index(\"11\"), make_index(\"53\")});\nfunctor_type Type::string_ = make_prim(\"String\");\n\natomic_size_t Type::cnt;\n\nType Type::new_var() {\n    return Type{\"x\" + to_string(cnt++), true, {}};\n}\n\nfunctor_type Type::lookup(Symbol sym) {\n    switch (sym) {\n        case Symbol::boxed_bool:\n            return bool_;\n        case Symbol::boxed_i32:\n            return i32;\n        case Symbol::boxed_i64:\n            return i64;\n        case Symbol::boxed_fp32:\n            return fp32;\n        case Symbol::boxed_fp64:\n            return fp64;\n        case Symbol::boxed_string:\n            return string_;\n        case Symbol::model:\n        case Symbol::opaque_set:\n            throw std::runtime_error(\"shouldn't happen\");\n/* INSERT 0 */\n    }\n    __builtin_unreachable();\n}\n\n}"
  },
  {
    "path": "src/main/resources/codegen/src/Type.hpp",
    "content": "#pragma once\n\n#include <atomic>\n#include <cassert>\n#include <cstdlib>\n#include <map>\n#include <utility>\n#include <vector>\n\n#include \"Symbol.hpp\"\n\nnamespace flg {\n\nstruct Type;\nstruct TypeSubst;\n\ntypedef std::pair<std::vector<Type>, Type> functor_type;\n\nstruct Type {\n    string name;\n    bool is_var;\n    std::vector<Type> args;\n\n    static functor_type lookup(Symbol sym);\n\n    static functor_type i32;\n    static functor_type i64;\n    static functor_type fp32;\n    static functor_type fp64;\n    static functor_type string_;\n    static functor_type bool_;\n\nprivate:\n    static functor_type make_prim(const std::string &name);\n\n    static functor_type make_prim(const std::string &name, const std::vector<Type> &args);\n\n    static Type make_index(const std::string &name);\n\n    static Type new_var();\n\n    static std::atomic_size_t cnt;\n};\n\ninline bool operator<(const Type &lhs, const Type &rhs) {\n    return lhs.name < rhs.name;\n}\n\nstruct TypeSubst {\n    void put(const Type &var, const Type &other);\n\n    Type apply(const Type &type);\n\n    void clear();\n\nprivate:\n    std::map<Type, Type> m;\n};\n\nostream &operator<<(ostream &out, const Type &type);\n\n} // namespace flg\n"
  },
  {
    "path": "src/main/resources/codegen/src/formulog.dl",
    "content": ""
  },
  {
    "path": "src/main/resources/codegen/src/funcs.hpp",
    "content": "#pragma once\n\n#include <algorithm>\n#include <cstdlib>\n#include <limits>\n#include <sstream>\n#include <regex>\n\n#include \"globals.h\"\n#include \"Term.hpp\"\n#include \"smt_solver.h\"\n\nnamespace flg {\n\nnamespace funcs {\n\nusing namespace std;\n\ntemplate<size_t N>\nterm_ptr __access(term_ptr t1) {\n    return t1->as_complex().val[N];\n}\n\nterm_ptr beq(term_ptr t1, term_ptr t2) {\n    return Term::make<bool>(t1 == t2);\n}\n\nterm_ptr bneq(term_ptr t1, term_ptr t2) {\n    return Term::make<bool>(t1 != t2);\n}\n\nterm_ptr bnot(term_ptr t1) {\n    return Term::make<bool>(!t1->as_base<bool>().val);\n}\n\ntemplate<typename T>\nterm_ptr __add(term_ptr t1, term_ptr t2) {\n    return Term::make(t1->as_base<T>().val + t2->as_base<T>().val);\n}\n\ntemplate<typename T>\nterm_ptr __sub(term_ptr t1, term_ptr t2) {\n    return Term::make(t1->as_base<T>().val - t2->as_base<T>().val);\n}\n\ntemplate<typename T>\nterm_ptr __mul(term_ptr t1, term_ptr t2) {\n    return Term::make(t1->as_base<T>().val * t2->as_base<T>().val);\n}\n\ntemplate<typename T>\nterm_ptr __div(term_ptr t1, term_ptr t2) {\n    return Term::make(t1->as_base<T>().val / t2->as_base<T>().val);\n}\n\ntemplate<typename T>\nterm_ptr __rem(term_ptr t1, term_ptr t2) {\n    return Term::make(t1->as_base<T>().val % t2->as_base<T>().val);\n}\n\ntemplate<typename T>\nterm_ptr __bitwise_and(term_ptr t1, term_ptr t2) {\n    return Term::make(t1->as_base<T>().val & t2->as_base<T>().val);\n}\n\ntemplate<typename T>\nterm_ptr __bitwise_or(term_ptr t1, term_ptr t2) {\n    return Term::make(t1->as_base<T>().val | t2->as_base<T>().val);\n}\n\ntemplate<typename T>\nterm_ptr __bitwise_xor(term_ptr t1, term_ptr t2) {\n    return Term::make(t1->as_base<T>().val ^ t2->as_base<T>().val);\n}\n\ntemplate<typename T>\nterm_ptr __shl(term_ptr t1, term_ptr t2) {\n    return Term::make(t1->as_base<T>().val << t2->as_base<T>().val);\n}\n\ntemplate<typename T>\nterm_ptr __ashr(term_ptr t1, term_ptr t2) {\n    return Term::make(t1->as_base<T>().val >> t2->as_base<T>().val);\n}\n\ntemplate<typename T, typename U>\nterm_ptr __lshr(term_ptr t1, term_ptr t2) {\n    U res = t1->as_base<U>().val >> t2->as_base<T>().val;\n    return Term::make(reinterpret_cast<T&>(res));\n}\n\ntemplate<typename T, typename U>\nterm_ptr __urem(term_ptr t1, term_ptr t2) {\n    U res = t1->as_base<U>().val % t2->as_base<U>().val;\n    return Term::make(reinterpret_cast<T&>(res));\n}\n\ntemplate<typename T, typename U>\nterm_ptr __udiv(term_ptr t1, term_ptr t2) {\n    U res = t1->as_base<U>().val / t2->as_base<U>().val;\n    return Term::make(reinterpret_cast<T&>(res));\n}\n\ntemplate<typename T>\nterm_ptr __neg(term_ptr t1) {\n    return Term::make(-t1->as_base<T>().val);\n}\n\ntemplate<typename T>\nterm_ptr __eq(term_ptr t1, term_ptr t2) {\n    return Term::make<bool>(t1->as_base<T>().val == t2->as_base<T>().val);\n}\n\ntemplate<typename T>\nterm_ptr __lt(term_ptr t1, term_ptr t2) {\n    return Term::make(t1->as_base<T>().val < t2->as_base<T>().val);\n}\n\ntemplate<typename T>\nterm_ptr __le(term_ptr t1, term_ptr t2) {\n    return Term::make(t1->as_base<T>().val <= t2->as_base<T>().val);\n}\n\ntemplate<typename T>\nterm_ptr __gt(term_ptr t1, term_ptr t2) {\n    return Term::make(t1->as_base<T>().val > t2->as_base<T>().val);\n}\n\ntemplate<typename T>\nterm_ptr __ge(term_ptr t1, term_ptr t2) {\n    return Term::make(t1->as_base<T>().val >= t2->as_base<T>().val);\n}\n\ntemplate<typename T>\nterm_ptr __cmp(term_ptr t1, term_ptr t2) {\n#ifdef FLG_DEV\n    return nullptr;\n#else\n    auto xval = t1->as_base<T>().val;\n    auto yval = t2->as_base<T>().val;\n    if (xval < yval) {\n      return Term::make<Symbol::cmp_lt>();\n    } else if (xval > yval) {\n      return Term::make<Symbol::cmp_gt>();\n    } else {\n      return Term::make<Symbol::cmp_eq>();\n    }\n#endif\n}\n\nterm_ptr print(term_ptr t1) {\n    cout << *t1 << endl;\n    return Term::make<bool>(true);\n}\n\nterm_ptr string_concat(term_ptr t1, term_ptr t2) {\n    return Term::make_moved(t1->as_base<string>().val + t2->as_base<string>().val);\n}\n\nterm_ptr string_matches(term_ptr t1, term_ptr t2) {\n    regex re(t2->as_base<string>().val);\n    return Term::make<bool>(regex_match(t1->as_base<string>().val, re));\n}\n\nterm_ptr string_starts_with(term_ptr t1, term_ptr t2) {\n    auto str = t1->as_base<string>().val;\n    auto pre = t2->as_base<string>().val;\n    if (pre.size() > str.size()) {\n        return Term::make<bool>(false);\n    }\n    auto res = mismatch(pre.begin(), pre.end(), str.begin());\n    return Term::make<bool>(res.first == pre.end());\n}\n\nterm_ptr _make_some(term_ptr t) {\n#ifdef FLG_DEV\n    return nullptr;\n#else\n    return Term::make<Symbol::some>(t);\n#endif\n}\n\nterm_ptr _make_none() {\n#ifdef FLG_DEV\n    return nullptr;\n#else\n    return Term::make<Symbol::none>();\n#endif\n}\n\nterm_ptr char_at(term_ptr s, term_ptr i) {\n    auto str = s->as_base<string>().val;\n    auto pos = i->as_base<int32_t>().val;\n    if (pos < 0 || (size_t) pos >= str.size()) {\n        return _make_none();\n    }\n    return _make_some(Term::make<int32_t>(str[pos]));\n}\n\nterm_ptr string_length(term_ptr s) {\n    return Term::make<int32_t>(s->as_base<string>().val.size());\n}\n\nterm_ptr vec_to_term_list(const std::vector<term_ptr> &v) {\n#ifdef FLG_DEV\n    return nullptr;\n#else\n    term_ptr acc = Term::make<Symbol::nil>();\n    for (auto it = v.rbegin(); it != v.rend(); ++it) {\n        acc = Term::make<Symbol::cons>(*it, acc);\n    }\n    return acc;\n#endif\n}\n\nterm_ptr string_to_list(term_ptr s) {\n    vector<term_ptr> v;\n    auto str = s->as_base<string>().val;\n    for (char ch: str) {\n        v.push_back(Term::make<int32_t>(ch));\n    }\n    return vec_to_term_list(v);\n}\n\nterm_ptr list_to_string(term_ptr l) {\n    vector<term_ptr> v = Term::vectorize_list_term(l);\n    string str;\n    for (auto sub: v) {\n        str += (char) sub->as_base<int32_t>().val;\n    }\n    return Term::make_moved(std::move(str));\n}\n\nterm_ptr substring(term_ptr str_term, term_ptr start_term, term_ptr len_term) {\n    auto str = str_term->as_base<string>().val;\n    auto start = start_term->as_base<int32_t>().val;\n    auto len = len_term->as_base<int32_t>().val;\n    if (start < 0 || len < 0 || str.size() < start + (size_t) len) {\n        return _make_none();\n    }\n    return _make_some(Term::make_moved(str.substr(start, len)));\n}\n\nterm_ptr string_to_i32(term_ptr str_term) {\n    auto str = str_term->as_base<string>().val;\n    static regex dec(\"[+-]?\\\\d+\");\n    static regex hex(\"0x[0-9a-fA-F]+\");\n    try {\n        if (regex_match(str, dec)) {\n            long r = stol(str);\n            if (r >= INT32_MIN && r <= INT32_MAX) {\n                return _make_some(Term::make((int32_t) r));\n            }\n        } else if (regex_match(str, hex)) {\n            unsigned long r = stoul(str, nullptr, 16);\n            if (r <= UINT32_MAX) {\n                return _make_some(Term::make((int32_t) r));\n            }\n        }\n    } catch (out_of_range &_) {\n        // fall through\n    }\n    return _make_none();\n}\n\nterm_ptr string_to_i64(term_ptr str_term) {\n    auto str = str_term->as_base<string>().val;\n    static regex dec(\"[+-]?\\\\d+\");\n    static regex hex(\"0x[0-9a-fA-F]+\");\n    try {\n        if (regex_match(str, dec)) {\n            long long r = stoll(str);\n            if (r >= INT64_MIN && r <= INT64_MAX) {\n                return _make_some(Term::make((int64_t) r));\n            }\n        } else if (regex_match(str, hex)) {\n            unsigned long long r = stoull(str, nullptr, 16);\n            if (r <= UINT64_MAX) {\n                return _make_some(Term::make((int64_t) r));\n            }\n        }\n    } catch (out_of_range &_) {\n        // fall through\n    }\n    return _make_none();\n}\n\nterm_ptr to_string(term_ptr t1) {\n    if (t1->sym == Symbol::boxed_string) {\n        return t1;\n    }\n    stringstream ss;\n    ss << *t1;\n    return Term::make_moved(ss.str());\n}\n\ntemplate<typename S, typename T>\nterm_ptr __conv(term_ptr t1) {\n    return Term::make<T>(t1->as_base<S>().val);\n}\n\nterm_ptr is_sat(term_ptr t1) {\n    switch (smt::smt_solver.check(t1).status) {\n        case smt::SmtStatus::sat:\n            return Term::make<bool>(true);\n        case smt::SmtStatus::unsat:\n            return Term::make<bool>(false);\n        case smt::SmtStatus::unknown:\n            throw runtime_error(\"SMT returned `unknown`\");\n    }\n    __builtin_unreachable();\n}\n\nterm_ptr _make_smt_not(term_ptr t) {\n#ifdef FLG_DEV\n    return nullptr;\n#else\n    return Term::make<Symbol::smt_not>(t);\n#endif\n}\n\nterm_ptr is_valid(term_ptr t1) {\n    switch (smt::smt_solver.check(_make_smt_not(t1)).status) {\n        case smt::SmtStatus::sat:\n            return Term::make<bool>(false);\n        case smt::SmtStatus::unsat:\n            return Term::make<bool>(true);\n        case smt::SmtStatus::unknown:\n            abort();\n    }\n    __builtin_unreachable();\n}\n\nint32_t _extract_timeout_from_option(term_ptr o) {\n    int32_t timeout{numeric_limits<int32_t>::max()};\n#ifndef FLG_DEV\n    if (o->sym == Symbol::some) {\n        auto &x = o->as_complex();\n        timeout = x.val[0]->as_base<int32_t>().val;\n    }\n#endif\n    return timeout;\n}\n\nterm_ptr is_sat_opt(term_ptr t1, term_ptr t2) {\n    int timeout = _extract_timeout_from_option(t2);\n    auto assertions = Term::vectorize_list_term(t1);\n    std::reverse(assertions.begin(), assertions.end());\n    switch (smt::smt_solver.check(assertions, false, timeout).status) {\n        case smt::SmtStatus::sat:\n            return _make_some(Term::make<bool>(true));\n        case smt::SmtStatus::unsat:\n            return _make_some(Term::make<bool>(false));\n        case smt::SmtStatus::unknown:\n            return _make_none();\n    }\n    __builtin_unreachable();\n}\n\nterm_ptr is_set_sat(term_ptr t1, term_ptr t2) {\n    int timeout = _extract_timeout_from_option(t2);\n    auto &s = t1->as_base<Set>().val;\n    std::vector<term_ptr> assertions{s.begin(), s.end()};\n    switch (smt::smt_solver.check(assertions, false, timeout).status) {\n        case smt::SmtStatus::sat:\n            return _make_some(Term::make<bool>(true));\n        case smt::SmtStatus::unsat:\n            return _make_some(Term::make<bool>(false));\n        case smt::SmtStatus::unknown:\n            return _make_none();\n    }\n    __builtin_unreachable();\n}\n\nterm_ptr get_model(term_ptr t1, term_ptr t2) {\n    int timeout = _extract_timeout_from_option(t2);\n    auto assertions = Term::vectorize_list_term(t1);\n    std::reverse(assertions.begin(), assertions.end());\n    auto res = smt::smt_solver.check(assertions, true, timeout);\n    switch (res.status) {\n        case smt::SmtStatus::sat:\n            return _make_some(Term::make_moved<Model>(std::move(res.model.value())));\n        case smt::SmtStatus::unsat:\n        case smt::SmtStatus::unknown:\n            return _make_none();\n    }\n    __builtin_unreachable();\n}\n\nterm_ptr query_model(term_ptr t1, term_ptr t2) {\n    Model m = t2->as_base<Model>().val;\n    auto it = m.find(t1);\n    if (it != m.end()) {\n        return _make_some(it->second);\n    }\n    return _make_none();\n}\n\nstd::vector<souffle::RamDomain> make_int_key(std::vector<term_ptr> key) {\n    unsigned arity = key.size();\n    std::vector<souffle::RamDomain> intKey(arity);\n    for (unsigned i = 0; i < arity; ++i) {\n        if (key[i]) {\n            intKey[i] = key[i]->intize();\n        }\n    }\n    return intKey;\n}\n\nterm_ptr _relation_contains(const std::string &relname, const std::vector<term_ptr> &key) {\n    auto rel = globals::program->getRelation(relname);\n    assert(rel);\n    size_t arity = rel->getPrimaryArity();\n    assert(arity == key.size());\n    std::vector<souffle::RamDomain> intKey = make_int_key(key);\n    for (auto &tup: *rel) {\n        bool match = true;\n        for (unsigned i = 0; i < arity; ++i) {\n            match &= !key[i] || intKey[i] == tup[i];\n        }\n        if (match) {\n            return Term::make(true);\n        }\n    }\n    return Term::make(false);\n}\n\nterm_ptr _relation_contains_complete(const std::string &relname, const std::vector<term_ptr> &key) {\n    auto rel = globals::program->getRelation(relname);\n    assert(rel);\n    size_t arity = rel->getPrimaryArity();\n    assert(arity == key.size());\n    std::vector<souffle::RamDomain> intKey = make_int_key(key);\n    souffle::tuple tup(rel);\n    for (auto arg: intKey) {\n        tup << arg;\n    }\n    return Term::make(rel->contains(tup));\n}\n\nterm_ptr _relation_agg_mono(const std::string &relname, const std::vector<term_ptr> &key, unsigned pos) {\n    auto rel = globals::program->getRelation(relname);\n    assert(rel);\n    size_t arity = rel->getPrimaryArity();\n    assert(arity == key.size());\n    assert(pos < arity);\n    std::vector<souffle::RamDomain> intKey = make_int_key(key);\n    std::vector<term_ptr> v;\n    for (auto &tup: *rel) {\n        bool match = true;\n        for (unsigned i = 0; i < arity; ++i) {\n            match &= !key[i] || intKey[i] == tup[i];\n        }\n        if (match) {\n            v.push_back(Term::unintize(tup[pos]));\n        }\n    }\n    return vec_to_term_list(v);\n}\n\ntemplate<Symbol S>\nterm_ptr\n_relation_agg_poly(const std::string &relname, const std::vector<term_ptr> &key, const std::vector<bool> &projection) {\n    auto rel = globals::program->getRelation(relname);\n    assert(rel);\n    size_t arity = rel->getPrimaryArity();\n    assert(arity == key.size());\n    assert(projection.size() == arity);\n    std::vector<souffle::RamDomain> intKey = make_int_key(key);\n    std::vector<term_ptr> v;\n    for (auto &tup: *rel) {\n        bool match = true;\n        for (unsigned i = 0; i < arity; ++i) {\n            match &= !key[i] || intKey[i] == tup[i];\n        }\n        if (match) {\n            std::vector<term_ptr> args;\n            for (unsigned i = 0; i < arity; ++i) {\n                if (projection[i]) {\n                    args.push_back(Term::unintize(tup[i]));\n                }\n            }\n            v.push_back(Term::make_generic(S, args));\n        }\n    }\n    return vec_to_term_list(v);\n}\n\nterm_ptr opaque_set_empty() {\n    return Term::make_moved(set::empty());\n}\n\nterm_ptr opaque_set_plus(term_ptr val, term_ptr set) {\n    auto &s = set->as_base<Set>().val;\n    return Term::make_moved(set::plus(val, s));\n}\n\nterm_ptr opaque_set_minus(term_ptr val, term_ptr set) {\n    auto &s = set->as_base<Set>().val;\n    return Term::make_moved(set::minus(val, s));\n}\n\nterm_ptr opaque_set_union(term_ptr set1, term_ptr set2) {\n    auto &s1 = set1->as_base<Set>().val;\n    auto &s2 = set2->as_base<Set>().val;\n    return Term::make_moved(set::plus_all(s1, s2));\n}\n\nterm_ptr opaque_set_diff(term_ptr set1, term_ptr set2) {\n    auto &s1 = set1->as_base<Set>().val;\n    auto &s2 = set2->as_base<Set>().val;\n    return Term::make_moved(set::minus_all(s1, s2));\n}\n\nterm_ptr opaque_set_choose(term_ptr set) {\n    auto &s = set->as_base<Set>().val;\n    auto opt = set::choose(s);\n    if (opt.has_value()) {\n        auto p = opt.value();\n        auto t = p.first;\n        auto r = Term::make_moved(std::move(p.second));\n        auto sym = lookup_tuple_symbol(2);\n        return _make_some(Term::make_generic(sym, {t, r}));\n    } else {\n        return _make_none();\n    }\n}\n\nterm_ptr opaque_set_size(term_ptr set) {\n    auto &s = set->as_base<Set>().val;\n    return Term::make((int32_t) set::size(s));\n}\n\nterm_ptr opaque_set_member(term_ptr val, term_ptr set) {\n    auto &s = set->as_base<Set>().val;\n    return Term::make(set::member(val, s));\n}\n\nterm_ptr opaque_set_singleton(term_ptr val) {\n    return Term::make_moved(set::singleton(val));\n}\n\nterm_ptr opaque_set_subset(term_ptr set1, term_ptr set2) {\n    auto &s1 = set1->as_base<Set>().val;\n    auto &s2 = set2->as_base<Set>().val;\n    return Term::make(set::subset(s1, s2));\n}\n\nterm_ptr opaque_set_from_list(term_ptr list) {\n    auto vec = Term::vectorize_list_term(list);\n    return Term::make_moved(set::from_vec(vec));\n}\n\n// The template varargs is necessary to handle folding with closures (captured variables are passed in as additional\n// arguments).\ntemplate<typename... Ts>\nterm_ptr fold(term_ptr (*f)(term_ptr, term_ptr, Ts...), term_ptr acc, term_ptr list, Ts... args) {\n    auto vec = Term::vectorize_list_term(list);\n    for (auto t : vec) {\n        acc = f(acc, t, args...);\n    }\n    return acc;\n}\n/* INSERT 0 */\n\n} // namespace funcs\n/* INSERT 1 */\n\n} // namespace flg\n"
  },
  {
    "path": "src/main/resources/codegen/src/functors.cpp",
    "content": "#include \"functors.h\"\n#include \"funcs.hpp\"\n\nusing namespace flg;\nusing namespace std;\n\nsouffle::RamDomain\nnth(souffle::RamDomain n, souffle::RamDomain ref, souffle::RamDomain check) {\n    assert(ref && check);\n    return Term::unintize(ref)->as_complex().val[n]->intize();\n}\n\n/* INSERT 0 */"
  },
  {
    "path": "src/main/resources/codegen/src/functors.h",
    "content": "#pragma once\n\n#include <souffle/SouffleInterface.h>\n#include \"Term.hpp\"\n\nnamespace flg {\n\ntemplate<flg::Symbol S>\ninline souffle::RamDomain dtor(term_ptr t) {\n    return t->sym == S;\n}\n\n}\n\nextern \"C\" {\n\nsouffle::RamDomain\nnth(souffle::RamDomain n, souffle::RamDomain ref, souffle::RamDomain check);\n\n/* INSERT 0 */\n\n}"
  },
  {
    "path": "src/main/resources/codegen/src/globals.h",
    "content": "#pragma once\n\n#include <souffle/SouffleInterface.h>\n#include <tbb/combinable.h>\n#include \"smt_solver.h\"\n#include \"time.hpp\"\n\nnamespace flg::globals {\n\ninline souffle::SouffleProgram *program{nullptr};\n\ninline smt::SmtSolverMode smt_solver_mode{smt::SmtSolverMode::check_sat_assuming};\n\ninline bool smt_double_check{true};\n\ninline size_t smt_cache_size{100};\n\ninline bool smt_stats{false};\n\nstruct smt_stats_t {\n    time_t time;\n    unsigned long long ncalls;\n\n    friend smt_stats_t &operator+=(smt_stats_t &stats, time_t time) {\n        stats.time += time;\n        stats.ncalls++;\n        return stats;\n    }\n};\n\ninline tbb::combinable<smt_stats_t> smt_time;\ninline tbb::combinable<time_t> smt_wait_time;\ninline tbb::combinable<unsigned> smt_cache_hits;\ninline tbb::combinable<unsigned> smt_cache_misses;\ninline tbb::combinable<unsigned> smt_cache_clears;\n\ntemplate<typename T>\nT sum(tbb::combinable<T> &stat) {\n    return stat.combine([](T x, T y) -> T { return x + y; });\n}\n\n}"
  },
  {
    "path": "src/main/resources/codegen/src/main.cpp",
    "content": "#include <chrono>\n#include <fstream>\n#include <iostream>\n#include <utility>\n#include <vector>\n#include <string>\n\n#include <boost/asio.hpp>\n#include <boost/filesystem.hpp>\n#include <boost/format.hpp>\n#include <boost/program_options.hpp>\n#include <omp.h>\n\n#include \"parser.hpp\"\n#include \"functors.h\"\n#include \"globals.h\"\n\nusing namespace flg;\nusing namespace std;\n\nstruct ExternalEdbLoader {\n    explicit ExternalEdbLoader(size_t nthreads) : pool(nthreads) {}\n\n    void go(const vector<string> &dirs);\n\nprivate:\n    boost::asio::thread_pool pool;\n\n    void loadEdbs(const string &dir);\n\n    void loadEdbs(const string &dir, const string &file, souffle::Relation *rel);\n};\n\nvoid ExternalEdbLoader::go(const vector<string> &dirs) {\n    for (auto &dir: dirs) {\n        loadEdbs(dir);\n    }\n    pool.join();\n}\n\nvoid ExternalEdbLoader::loadEdbs(const string &dir) {\n/* INSERT 0 */\n}\n\nvoid ExternalEdbLoader::loadEdbs(const string &dir, const string &file, souffle::Relation *rel) {\n    assert(rel);\n    boost::filesystem::path path{dir};\n    path /= file;\n    string pathstr = path.string();\n    boost::asio::post(pool, [pathstr, rel]() {\n        ifstream stream(pathstr);\n        parse_facts(stream, *rel);\n        stream.close();\n    });\n}\n\nvoid loadFact(const string &relname, const vector<term_ptr> &args) {\n    auto rel = globals::program->getRelation(relname);\n    assert(rel);\n    souffle::tuple tup(rel);\n    for (auto arg: args) {\n        tup << arg->intize();\n    }\n    rel->insert(tup);\n}\n\nvoid loadEdbs(const vector<string> &dirs, size_t nthreads) {\n    ExternalEdbLoader(nthreads).go(dirs);\n/* INSERT 1 */\n}\n\nvoid printBanner(const std::string &heading) {\n    std::cout << \"==================== \" << heading << \" ====================\\n\";\n}\n\nvoid printSmallBanner(const std::string &heading) {\n    std::cout << \"---------- \" << heading << \" ----------\\n\";\n}\n\nvoid printSizes() {\n    std::cout << \"\\n\";\n    printBanner(\"RELATION SIZES\");\n    for (auto rel: globals::program->getOutputRelations()) {\n        std::string name = rel->getName();\n        if (name.find(\"CODEGEN_\") == 0) {\n            continue;\n        }\n        name = name.substr(0, name.size() - 1);\n        std::cout << name << \": \" << rel->size() << std::endl;\n    }\n}\n\nvoid printResults() {\n    std::cout << \"\\n\";\n    printBanner(\"SELECTED IDB RELATIONS\");\n    for (auto rel: globals::program->getOutputRelations()) {\n        std::string name = rel->getName();\n        if (name.find(\"CODEGEN_\") == 0) {\n            continue;\n        }\n        name = name.substr(0, name.size() - 1);\n        std::stringstream ss;\n        ss << name  << \" (\" << rel->size() << \")\";\n        std::cout << \"\\n\";\n        printSmallBanner(ss.str());\n        for (auto &tup: *rel) {\n            std::cout << name << \"(\";\n            for (size_t i = 0; i < rel->getPrimaryArity(); ++i) {\n                std::cout << *Term::unintize(tup[i]);\n                if (i < rel->getPrimaryArity() - 1) {\n                    std::cout << \", \";\n                }\n            }\n            std::cout << \")\" << std::endl;\n        }\n    }\n}\n\nstruct ExternalIdbPrinter {\n    ExternalIdbPrinter(boost::filesystem::path dir_, size_t nthreads) : dir(std::move(dir_)), pool(nthreads) {}\n\n    void go();\n\nprivate:\n    const boost::filesystem::path dir;\n\n    boost::asio::thread_pool pool;\n\n    void saveToDisk(const string &name);\n};\n\nvoid ExternalIdbPrinter::saveToDisk(const string &name) {\n    auto rel = globals::program->getRelation(name);\n    assert(rel);\n    auto path(dir);\n    auto corrected_name = name.substr(0, name.size() - 1);\n    path /= corrected_name;\n    auto pathstr = path.string() + string(\".tsv\");\n    boost::asio::post(pool, [pathstr, rel]() {\n        ofstream ofs(pathstr);\n        for (auto &tup: *rel) {\n            for (size_t i = 0; i < rel->getPrimaryArity(); ++i) {\n                ofs << *Term::unintize(tup[i]);\n                if (i < rel->getPrimaryArity() - 1) {\n                    ofs << \"\\t\";\n                }\n            }\n            ofs << \"\\n\";\n        }\n        ofs.flush();\n        ofs.close();\n    });\n}\n\nvoid ExternalIdbPrinter::go() {\n/* INSERT 2 */\n    pool.join();\n}\n\nnamespace std {\nstd::ostream &operator<<(std::ostream &os, const std::vector<std::string> &vec) {\n    for (auto &item: vec) {\n        os << item << \" \";\n    }\n    return os;\n}\n}\n\ntemplate <typename T>\nstd::string join(const std::vector<T> &v) {\n    if (v.empty()) {\n        return \"\";\n    }\n    std::stringstream ss;\n    auto it = v.begin();\n    while (true) {\n        ss << *it++;\n        if (it == v.end()) {\n            break;\n        }\n        ss << \",\";\n    }\n    return ss.str();\n}\n\nvoid printSmtStats() {\n    std::vector<double> times;\n    std::vector<unsigned long long> calls;\n    flg::time_t total_time;\n    double total_calls;\n    globals::smt_time.combine_each([&](auto stats) {\n        times.push_back(stats.time.count());\n        total_time += stats.time;\n        calls.push_back(stats.ncalls);\n        total_calls += stats.ncalls;\n    });\n\n    std::cout << \"\\n\";\n    printBanner(\"SMT STATS\");\n    std::cout << \"SMT calls: \" << total_calls << \"\\n\";\n    std::cout << \"SMT time (ms): \" << total_time.count() << std::endl;\n    std::cout << \"SMT wait time (ms): \" << globals::sum(globals::smt_wait_time).count() << std::endl;\n    std::cout << \"SMT cache hits: \" << globals::sum(globals::smt_cache_hits) << std::endl;\n    std::cout << \"SMT cache misses: \" << globals::sum(globals::smt_cache_misses) << std::endl;\n    std::cout << \"SMT cache clears: \" << globals::sum(globals::smt_cache_clears) << std::endl;\n    std::cout << \"SMT calls per solver: \" << join(calls) << std::endl;\n    std::cout << \"SMT time per solver (ms): \" << join(times) << std::endl;\n}\n\nint main(int argc, char **argv) {\n    namespace po = boost::program_options;\n    po::options_description desc(\"Allowed options\");\n    string cwd = boost::filesystem::current_path().string();\n    bool dump_idb{false};\n    bool dump_sizes{false};\n    bool no_smt_double_check{false};\n    desc.add_options()\n            (\"help,h\", \"produce help message\")\n            (\"parallelism,j\", po::value<size_t>()->default_value(1),\n             \"number of threads to use\")\n            (\"dump-idb\", po::bool_switch(&dump_idb),\n                    \"print the contents of the IDB relations to screen\")\n            (\"dump-sizes\", po::bool_switch(&dump_sizes), \"print relation sizes\")\n            (\"fact-dir,F\", po::value<vector<string>>()->default_value({cwd}),\n             \"input directory with external EDBs (can be set multiple times)\")\n            (\"out-dir,D\", po::value<string>()->default_value(cwd),\n             \"directory for .tsv output files\")\n            (\"smt-solver-mode\", po::value<smt::SmtSolverMode>()->default_value(smt::SmtSolverMode::check_sat_assuming),\n             \"set interaction strategy between Formulog and the external SMT solver\")\n            (\"no-smt-double-check\", po::bool_switch(&no_smt_double_check),\n             \"do not double check unknown values returned by SMT solver (using a generally more reliable solver mode)\")\n            (\"smt-cache-size\", po::value<size_t>()->default_value(100),\n             \"how many implications to store for check-sat-assuming solver\")\n            (\"smt-stats\", po::bool_switch(&globals::smt_stats),\n             \"report basic statistics related to SMT solver usage\");\n\n    po::variables_map vm;\n    po::store(po::parse_command_line(argc, argv, desc), vm);\n    po::notify(vm);\n\n    if (vm.count(\"help\")) {\n        cout << desc << endl;\n        return 1;\n    }\n\n    size_t parallelism = vm[\"parallelism\"].as<size_t>();\n    if (parallelism == 0) {\n        cout << \"Cannot use 0 threads\" << endl;\n        return 1;\n    }\n\n    initialize_symbols();\n    globals::smt_solver_mode = vm[\"smt-solver-mode\"].as<smt::SmtSolverMode>();\n    globals::smt_double_check = !no_smt_double_check;\n    globals::program = souffle::ProgramFactory::newInstance(\"formulog\");\n    if (!globals::program) {\n        cout << \"Unable to load Souffle program\" << endl;\n        exit(1);\n    }\n\n    cout << \"Parsing...\" << endl;\n    auto start = std::chrono::steady_clock::now();\n    loadEdbs(vm[\"fact-dir\"].as<vector<string>>(), parallelism);\n    auto end = chrono::steady_clock::now();\n    double diff = chrono::duration_cast<chrono::milliseconds>(end - start).count() / 1000.0;\n    cout << \"Finished parsing (\" << boost::format(\"%.3f\") % diff << \"s)\" << endl;\n\n    cout << \"Evaluating...\" << endl;\n    globals::program->setNumThreads(parallelism);\n    start = std::chrono::steady_clock::now();\n    globals::program->run();\n    end = chrono::steady_clock::now();\n    diff = chrono::duration_cast<chrono::milliseconds>(end - start).count() / 1000.0;\n    cout << \"Finished evaluating (\" << boost::format(\"%.3f\") % diff << \"s)\" << endl;\n\n    flg::smt::SmtLibShim::terminate_all_children();\n    boost::filesystem::path out_dir(vm[\"out-dir\"].as<string>());\n    boost::filesystem::create_directories(out_dir);\n    ExternalIdbPrinter idbPrinter(out_dir, parallelism);\n    idbPrinter.go();\n\n    if (globals::smt_stats) {\n        printSmtStats();\n    }\n    if (dump_sizes) {\n        printSizes();\n    }\n    if (dump_idb) {\n        printResults();\n    }\n#ifdef FLG_RECORD_WORK\n    cout << \"[WORK] \" << globals::sum(souffle::work) << std::endl;\n#endif\n    std::_Exit(EXIT_SUCCESS);\n}\n"
  },
  {
    "path": "src/main/resources/codegen/src/parser.cpp",
    "content": "#include <algorithm>\n#include <cassert>\n#include <cmath>\n#include <regex>\n#include <unordered_map>\n#include <vector>\n\n#include <boost/optional.hpp>\n\n#include \"parser.hpp\"\n\nnamespace flg {\n\nnamespace parser {\n\nusing boost::optional;\n\n// A class that represents the intermediate parsing state of a term\nclass TermParser {\n    const string &buffer;\n    size_t pos;\n\npublic:\n    TermParser(const string &term) : buffer(term), pos(0) {}\n\n    inline void take_whitespace();\n\n    inline bool take_string(const string &str);\n\n    inline optional<smatch> take_regex(const regex &rgx);\n\n    inline char peek();\n\n    inline bool empty();\n\n    inline term_ptr take_term();\n\n    inline vector<term_ptr> take_terms();\n\n    inline term_ptr take_tuple_or_parens();\n\n    inline term_ptr take_list();\n\n    inline term_ptr take_constructor();\n\n    inline term_ptr take_string();\n\n    inline term_ptr take_numeric();\n};\n\ninline void TermParser::take_whitespace() {\n    while (pos < buffer.size() && buffer[pos] == ' ') {\n        ++pos;\n    }\n}\n\ninline bool TermParser::take_string(const string &str) {\n    if (\n            pos + str.size() <= buffer.size() &&\n            equal(str.begin(), str.end(), buffer.begin() + pos)\n            ) {\n        pos += str.size();\n        return true;\n    }\n    return false;\n}\n\ninline optional<smatch> TermParser::take_regex(const regex &rgx) {\n    smatch m;\n    if (regex_search(buffer.begin() + pos, buffer.end(), m, rgx)) {\n        pos += m.length();\n        return m;\n    }\n    return {};\n}\n\ninline char TermParser::peek() {\n    take_whitespace();\n    if (pos == buffer.size()) {\n        throw parsing_error(\"Unexpected end of term\");\n    }\n    return buffer[pos];\n}\n\ninline bool TermParser::empty() {\n    take_whitespace();\n    return pos == buffer.size();\n}\n\ninline term_ptr TermParser::take_term() {\n    take_whitespace();\n    term_ptr retval;\n\n    // Determine type of term based on first character\n    char c = peek();\n    if (c == '\"') {\n        retval = take_string();\n    } else if (c == '+' || c == '-' || ('0' <= c && c <= '9')) {\n        retval = take_numeric();\n    } else if (c == '(') {\n        retval = take_tuple_or_parens();\n    } else if (c == '[') {\n        retval = take_list();\n    } else if (c >= 'a' && c <= 'z') {\n        retval = take_constructor();\n    } else {\n        throw parsing_error(string(\"Unexpected character: \") + c);\n    }\n\n    // Handle right-associative cons (::) operator\n    if (!empty() && peek() == ':') {\n        ++pos;\n        if (peek() != ':') {\n            throw parsing_error(\"Found first ':', expected a second ':' for cons\");\n        }\n        ++pos;\n#ifndef FLG_DEV\n        return Term::make<Symbol::cons>(retval, take_term());\n#endif\n    }\n\n    return retval;\n}\n\ninline vector<term_ptr> TermParser::take_terms() {\n    vector<term_ptr> retval;\n    retval.push_back(take_term());\n    while (!empty() && peek() == ',') {\n        ++pos;\n        retval.push_back(take_term());\n    }\n    return retval;\n}\n\ninline term_ptr TermParser::take_tuple_or_parens() {\n    assert(buffer[pos] == '('); // Sanity check\n    ++pos;\n    vector<term_ptr> terms = take_terms();\n    if (peek() != ')') {\n        throw parsing_error(\"Expected character ')' to close parens\");\n    }\n    ++pos;\n    if (terms.size() == 1) {\n        // Parentheses case\n        return terms[0];\n    }\n    // Tuple case\n    auto sym = lookup_tuple_symbol(terms.size());\n    return Term::make_generic(sym, terms);\n}\n\ninline term_ptr TermParser::take_list() {\n    assert(buffer[pos] == '['); // Sanity check\n    ++pos;\n    // Case: empty list (equivalent to nil)\n    if (peek() == ']') {\n        ++pos;\n#ifndef FLG_DEV\n        return Term::make<Symbol::nil>();\n#endif\n    }\n    vector<term_ptr> terms = take_terms();\n    if (peek() != ']') {\n        throw parsing_error(\"Expected character ']' to close list\");\n    }\n    ++pos;\n    // Construct the list\n#ifdef FLG_DEV\n    term_ptr retval = nullptr;\n#else\n    term_ptr retval = Term::make<Symbol::nil>();\n    for (auto it = terms.crbegin(); it != terms.crend(); ++it) {\n        retval = Term::make<Symbol::cons>(*it, retval);\n    }\n#endif\n    return retval;\n}\n\ninline term_ptr TermParser::take_constructor() {\n    static const unordered_map<string, term_ptr> literals = {\n            {\"true\",              Term::make<bool>(true)},\n            {\"false\",             Term::make<bool>(false)},\n            {\"true\",              Term::make<bool>(true)},\n            {\"fp32_nan\",          Term::make<float>(nanf(\"\"))},\n            {\"fp32_pos_infinity\", Term::make<float>(INFINITY)},\n            {\"fp32_neg_infinity\", Term::make<float>(-INFINITY)},\n            {\"fp64_nan\",          Term::make<double>(nan(\"\"))},\n            {\"fp64_pos_infinity\", Term::make<double>(INFINITY)},\n            {\"fp64_neg_infinity\", Term::make<double>(-INFINITY)},\n    };\n    string name;\n    while (pos < buffer.size()) {\n        char c = buffer[pos];\n        if (\n                ('a' <= c && c <= 'z') ||\n                ('A' <= c && c <= 'Z') ||\n                ('0' <= c && c <= '9') ||\n                (c == '_')\n                ) {\n            name += c;\n            ++pos;\n        } else {\n            break;\n        }\n    }\n    const auto it = literals.find(name);\n    if (it != literals.end()) {\n        return it->second;\n    }\n    auto sym = lookup_symbol(name);\n    vector<term_ptr> terms;\n    if (!empty() && peek() == '(') {\n        ++pos;\n        if (symbol_arity(sym) != 0) {\n            terms = take_terms();\n        }\n        if (peek() != ')') {\n            throw parsing_error(\"Expected ')' to close constructor\");\n        }\n        ++pos;\n    }\n    return Term::make_generic(sym, terms);\n}\n\ninline term_ptr TermParser::take_string() {\n    // Compatibility Note:\n    //   Currently, the Formulog parser does not support escape sequences, so I am\n    //   also not supporting escape sequences in the parsing step. This makes it\n    //   impossible to create strings with the characters \\\", \\n, \\t, \\r, etc.\n    assert(buffer[pos] == '\"');\n    ++pos;\n    string contents;\n    while (pos < buffer.size() && buffer[pos] != '\"') {\n        contents += buffer[pos++];\n    }\n    if (pos == buffer.size()) {\n        throw parsing_error(\"Could not detect end of string literal\");\n    }\n    ++pos;\n    return Term::make_moved<string>(std::move(contents));\n}\n\ninline term_ptr TermParser::take_numeric() {\n    // Parse sign\n    int sign = 1;\n    const char signchar = peek();\n    if (signchar == '+' || signchar == '-') {\n        if (signchar == '-') {\n            sign = -1;\n        }\n        ++pos;\n    }\n\n    // Handle hexadecimal case\n    static const regex hex_re(R\"(^0(?:x|X)([0-9a-fA-F]+)([Ll]?)\\b)\");\n    if (auto match = take_regex(hex_re)) {\n        if (match->length(2)) {\n            auto value = stoll(match->str(1), nullptr, 16);\n            return Term::make<int64_t>(sign * value);\n        } else {\n            auto value = stoi(match->str(1), nullptr, 16);\n            return Term::make<int32_t>(sign * value);\n        }\n    }\n\n    // Handle decimal case\n    static const regex dec_re(R\"(^([0-9]+)([Ll]?)\\b(?!\\.))\");\n    if (auto match = take_regex(dec_re)) {\n        if (match->length(2)) {\n            auto value = stoll(match->str(1));\n            return Term::make<int64_t>(sign * value);\n        } else {\n            auto value = stoi(match->str(1));\n            return Term::make<int32_t>(sign * value);\n        }\n    }\n\n    // Handle floating-point case\n    static const regex fp_re(\n            R\"(^((?:[0-9]*\\.[0-9]+|[0-9]+)(?:[Ee][+-]?[0-9]+)?)([FfDd]?)\\b)\"\n    );\n    if (auto match = take_regex(fp_re)) {\n        string suffix = match->str(2);\n        if (suffix == \"f\" || suffix == \"F\") {\n            auto value = stof(match->str(1));\n            return Term::make<float>(sign * value);\n        } else {\n            auto value = stod(match->str(1));\n            return Term::make<double>(sign * value);\n        }\n    }\n\n    throw parsing_error(\"Could not identify numeric value\");\n}\n\nterm_ptr parse_term(const string &term) {\n    TermParser context(term);\n    term_ptr retval = context.take_term();\n    if (!context.empty()) {\n        throw parsing_error(\"Unexpected trailing characters in term: \" + term);\n    }\n    return retval;\n}\n\n} // namespace parser\n\n} // namespace flg\n"
  },
  {
    "path": "src/main/resources/codegen/src/parser.hpp",
    "content": "#pragma once\n\n#include <iostream>\n#include <stdexcept>\n#include <sstream>\n#include <string>\n\n#include \"Term.hpp\"\n\nnamespace flg {\n\nnamespace parser {\n\nusing namespace std;\n\n// Exception class thrown when parsing facts\nclass parsing_error : public logic_error {\n    using logic_error::logic_error;\n};\n\nterm_ptr parse_term(const string &term);\n\ninline void parse_facts(istream &in, souffle::Relation &rel) {\n    // Parse each fact of the stream on a new line\n    size_t arity = rel.getPrimaryArity();\n    string line;\n    while (getline(in, line)) {\n        istringstream ss(line);\n        souffle::tuple tup(&rel);\n        // Tab-separated term format\n        string term;\n        size_t count = 0;\n        while (getline(ss, term, '\\t')) {\n            if (count >= arity) {\n                throw parsing_error(\"Too many terms in tab-separated line\");\n            }\n            tup << parse_term(term)->intize();\n            count++;\n        }\n        if (count != arity) {\n            throw parsing_error(\"Too few terms in tab-separated line\");\n        }\n        rel.insert(tup);\n    }\n}\n\n} // namespace parser\n\nusing parser::parse_facts;\n\n} // namespace flg\n"
  },
  {
    "path": "src/main/resources/codegen/src/set.cpp",
    "content": "//\n// Created by Aaron Bembenek on 2/10/23.\n//\n\n#include \"set.hpp\"\n\nnamespace flg::set {\n\nSet empty() {\n    return {};\n}\n\nSet singleton(term_ptr val) {\n    return { val };\n}\n\nSet plus(term_ptr val, const Set &set) {\n    Set r{set};\n    r.emplace(val);\n    return r;\n}\n\nSet minus(term_ptr val, const Set &set) {\n    Set r{set};\n    r.erase(val);\n    return r;\n}\n\nSet plus_all(const Set &s1, const Set &s2) {\n    Set r{s1};\n    r.insert(boost::container::ordered_unique_range, s2.begin(), s2.end());\n    return r;\n}\n\nSet minus_all(const Set &s1, const Set &s2) {\n    Set r{s1};\n    auto it = r.begin();\n    for (auto elt : s2)\n    {\n        it = std::lower_bound(it, r.end(), elt);\n        if (it == r.end()) {\n            break;\n        }\n        if (*it != elt) {\n            continue;\n        }\n        it = r.erase(it);\n    }\n    return r;\n}\n\nbool member(term_ptr val, const Set &set) {\n    return set.contains(val);\n}\n\nstd::size_t size(const Set &set) {\n    return set.size();\n}\n\nbool subset(const Set &s1, const Set &s2) {\n    auto it = s2.begin();\n    for (auto elt : s1)\n    {\n        it = std::lower_bound(it, s2.end(), elt);\n        if (it == s2.end() || *it != elt)\n        {\n            return false;\n        }\n    }\n    return true;\n}\n\nstd::optional<std::pair<term_ptr, Set>> choose(const Set &s) {\n    if (s.empty()) {\n        return {};\n    }\n    Set r{s};\n    auto it = r.end();\n    --it;\n    auto t = *it;\n    r.erase(it);\n    return {{t, r}};\n}\n\nSet from_vec(const std::vector<term_ptr> &vec) {\n    return {vec.begin(), vec.end()};\n}\n\n}"
  },
  {
    "path": "src/main/resources/codegen/src/set.hpp",
    "content": "//\n// Created by Aaron Bembenek on 2/10/23.\n//\n\n#ifndef SMT_PARSER_CPP_SET_H\n#define SMT_PARSER_CPP_SET_H\n\n#include <boost/container/flat_set.hpp>\n#include <optional>\n#include <vector>\n\nnamespace flg {\n\nstruct Term;\n\ntypedef const flg::Term *term_ptr;\ntypedef boost::container::flat_set<term_ptr> Set;\n\nnamespace set {\n\nSet empty();\n\nSet singleton(term_ptr val);\n\nSet plus(term_ptr val, const Set &set);\n\nSet minus(term_ptr val, const Set &set);\n\nSet plus_all(const Set &s1, const Set &s2);\n\nSet minus_all(const Set &s1, const Set &s2);\n\nbool member(term_ptr val, const Set &set);\n\nstd::size_t size(const Set &set);\n\nbool subset(const Set &s1, const Set &s2);\n\nstd::optional<std::pair<term_ptr, Set>> choose(const Set &s);\n\nSet from_vec(const std::vector<term_ptr> &vec);\n\n}\n}\n\n#endif //SMT_PARSER_CPP_SET_H\n"
  },
  {
    "path": "src/main/resources/codegen/src/smt_parser.cpp",
    "content": "//\n// Created by Aaron Bembenek on 1/23/23.\n//\n\n#include \"smt_parser.hpp\"\n#include <sstream>\n\nnamespace flg {\n\nvoid SmtLibTokenizer::load(bool allow_eof) {\n    if (m_next.has_value()) {\n        return;\n    }\n    int ch;\n    while (std::isspace(ch = m_is.get()) && m_ignore_whitespace) {\n        // keep looping\n    }\n    if (ch == std::char_traits<char>::eof()) {\n        if (allow_eof) {\n            return;\n        } else {\n            throw std::runtime_error(\"SMT-LIB tokenization error: unexpected EOF\");\n        }\n    }\n    std::string s;\n    s.push_back((char) ch);\n    if (is_word_char(ch)) {\n        while ((ch = m_is.peek()) != std::char_traits<char>::eof() && is_word_char(ch)) {\n            s.push_back((char) ch);\n            m_is.get();\n        }\n    }\n    m_next = std::move(s);\n}\n\nconst std::string &SmtLibTokenizer::peek() {\n    load(false);\n    return *m_next;\n}\n\nstd::string SmtLibTokenizer::next() {\n    load(false);\n    std::string s = *std::move(m_next);\n    m_next.reset();\n    return s;\n}\n\nbool SmtLibTokenizer::has_next() {\n    load(true);\n    return m_next.has_value();\n}\n\nvoid SmtLibTokenizer::consume(const std::string &s) {\n    std::stringstream ss(s);\n    SmtLibTokenizer t(ss);\n    t.ignore_whitespace(m_ignore_whitespace);\n    while (t.has_next()) {\n        std::string expected = t.next();\n        std::string found = next();\n        if (expected != found) {\n            std::stringstream msg;\n            msg << \"SMT-LIB tokenization error: tried to consume \\\"\" << expected << \"\\\" but found \\\"\" << found << \"\\\"\";\n            throw std::runtime_error(msg.str());\n        }\n    }\n}\n\nModel SmtLibParser::get_model(std::istream &is) const {\n    SmtLibTokenizer t(is);\n    t.consume(\"(\");\n    Model m;\n    while (true) {\n        const std::string &tok = t.peek();\n        if (tok == \")\") {\n            break;\n        } else if (tok == \";\") {\n            consume_comment(t);\n        } else {\n            parse_function_def(m, t);\n        }\n    }\n    t.consume(\")\");\n    assert(t.ignoring_whitespace());\n    t.ignore_whitespace(false);\n    // Remove EOL\n    t.next();\n    return m;\n}\n\nvoid SmtLibParser::consume_comment(SmtLibTokenizer &t) const {\n    t.consume(\";;\");\n    assert(t.ignoring_whitespace());\n    t.ignore_whitespace(false);\n    while (t.next() != \"\\n\") {\n        // Keep cruising\n    }\n    t.ignore_whitespace(true);\n}\n\nvoid skip_rest_of_s_exp(SmtLibTokenizer &t);\nstd::string parse_identifier(SmtLibTokenizer &t);\n\nvoid SmtLibParser::parse_function_def(Model &m, SmtLibTokenizer &t) const {\n    t.consume(\"(\");\n    auto tok = t.peek();\n    if (tok == \"forall\" || tok == \"declare\") {\n        skip_rest_of_s_exp(t);\n        return;\n    }\n    t.consume(\"define-fun\");\n    std::string id = parse_identifier(t);\n\n    // Ignore args\n    t.consume(\"(\");\n    skip_rest_of_s_exp(t);\n\n    // Ignore type\n    parse_type(t);\n\n    auto it = m_vars.find(id);\n    if (it != m_vars.end()) {\n        term_ptr x = it->second;\n        if (should_record(x->sym)) {\n            m.emplace(x, parse_term(t, x->sym));\n        }\n    }\n    skip_rest_of_s_exp(t);\n}\n\nstd::string parse_string_raw(SmtLibTokenizer &t) {\n    t.consume(\"\\\"\");\n    assert(t.ignoring_whitespace());\n    t.ignore_whitespace(false);\n    std::string s;\n    while (true) {\n        std::string tok = t.next();\n        if (tok == \"\\\"\") {\n            // SMT-LIB uses \"\" to represent the character \"\n            if (t.peek() != \"\\\"\") {\n                break;\n            }\n            t.consume(tok);\n        } else if (tok == \"\\\\\" && t.peek() == \"u\") {\n            // Handle unicode (in a hacky way). Not sure if we also need to account for the backslash being escaped.\n            t.consume(\"u\");\n            t.consume(\"{\");\n            std::string val;\n            while ((tok = t.next()) != \"}\") {\n                val += tok;\n            }\n            s.push_back((char) std::stoi(val, nullptr, 16));\n            continue;\n        }\n        s += tok;\n    }\n    t.ignore_whitespace(true);\n    return s;\n}\n\nbool is_ident_char(int ch) {\n    if (std::isalnum(ch)) {\n        return true;\n    }\n    switch (ch) {\n        case '~':\n        case '!':\n        case '@':\n        case '%':\n        case '^':\n        case '&':\n        case '*':\n        case '_':\n        case '-':\n        case '+':\n        case '=':\n        case '<':\n        case '>':\n        case '.':\n        case '?':\n        case '/':\n            return true;\n    }\n    return false;\n}\n\nstd::string parse_identifier(SmtLibTokenizer &t) {\n    std::string s = t.next();\n    assert(t.ignoring_whitespace());\n    t.ignore_whitespace(false);\n    if (s == \"|\") {\n        while (t.peek() != \"|\") {\n            s += t.next();\n        }\n        t.consume(\"|\");\n    } else {\n        while (true) {\n            const std::string &tok = t.peek();\n            if (is_ident_char(tok[0])) {\n                t.consume(tok);\n                s += tok;\n            } else {\n                if (std::isspace(tok[0])) {\n                    t.consume(tok);\n                }\n                break;\n            }\n        }\n    }\n    t.ignore_whitespace(true);\n    return s;\n}\n\n\nvoid skip_rest_of_s_exp(SmtLibTokenizer &t) {\n    unsigned depth = 1;\n    while (depth > 0) {\n        const std::string &tok = t.peek();\n        if (tok == \"(\") {\n            t.consume(tok);\n            depth++;\n        } else if (tok == \")\") {\n            t.consume(tok);\n            depth--;\n        } else if (tok == \"\\\"\") {\n            parse_string_raw(t);\n        } else if (tok == \"|\") {\n            parse_identifier(t);\n        } else {\n            t.consume(tok);\n        }\n    }\n}\n\nvoid SmtLibParser::parse_type(SmtLibTokenizer &t) const {\n    std::string tok = t.next();\n    if (tok == \"(\") {\n        skip_rest_of_s_exp(t);\n    }\n}\n\nbool SmtLibParser::should_record(Symbol sym) const {\n    switch (sym) {\n/* INSERT 0 */\n    default: return false;\n    }\n}\n\nterm_ptr parse_string(SmtLibTokenizer &t) {\n    return Term::make_moved<std::string>(parse_string_raw(t));\n}\n\nuint64_t parse_bv(SmtLibTokenizer &t) {\n    t.consume(\"#\");\n    std::string tok = t.next();\n    std::string prefix = tok.substr(0, 1);\n    std::string num = tok.substr(1);\n    int base{0};\n    if (prefix == \"b\") {\n        base = 2;\n    } else {\n        assert(prefix == \"x\");\n        base = 16;\n    }\n    return std::stoull(num, nullptr, base);\n}\n\ntemplate<typename To, typename From>\nTo bit_cast(From from) {\n    To dst;\n    memcpy(&dst, &from, sizeof(To));\n    return dst;\n}\n\nterm_ptr parse_i32(SmtLibTokenizer &t) {\n    return Term::make(bit_cast<int32_t>(parse_bv(t)));\n}\n\nterm_ptr parse_i64(SmtLibTokenizer &t) {\n    return Term::make(bit_cast<int64_t>(parse_bv(t)));\n}\n\ntemplate<typename T, unsigned E, unsigned S>\nterm_ptr parse_fp(SmtLibTokenizer &t) {\n    t.consume(\"(\");\n    T val;\n    if (t.peek() == \"fp\") {\n        t.consume(\"fp\");\n        uint64_t sign = parse_bv(t);\n        uint64_t exp = parse_bv(t);\n        uint64_t mant = parse_bv(t);\n        uint64_t bits = sign << (E + S - 1);\n        bits |=  exp << (S - 1);\n        bits |= mant;\n        val = bit_cast<T>(bits);\n    } else {\n        t.consume(\"_\");\n        std::string next = t.next();\n        if (next == \"NaN\") {\n            val = std::numeric_limits<T>::quiet_NaN();\n        } else if (next == \"+\") {\n            if (t.peek() == \"oo\") {\n                t.consume(\"oo\");\n                val = +std::numeric_limits<T>::infinity();\n            } else {\n                t.consume(\"zero\");\n                val = +0.0f;\n            }\n        } else {\n            assert(next == \"-\");\n            if (t.peek() == \"oo\") {\n                t.consume(\"oo\");\n                val = -std::numeric_limits<T>::infinity();\n            } else {\n                t.consume(\"zero\");\n                val = -0.0f;\n            }\n        }\n    }\n    skip_rest_of_s_exp(t);\n    return Term::make<T>(val);\n}\n\nterm_ptr parse_fp32(SmtLibTokenizer &t) {\n    return parse_fp<float, 8, 24>(t);\n}\n\nterm_ptr parse_fp64(SmtLibTokenizer &t) {\n    return parse_fp<double, 11, 53>(t);\n}\n\nterm_ptr parse_bool(SmtLibTokenizer &t) {\n    std::string tok = t.next();\n    if (tok == \"true\") {\n        return Term::make(true);\n    } else {\n        assert(tok == \"false\");\n        return Term::make(false);\n    }\n}\n\ntemplate<typename Fn>\nterm_ptr parse_adt(SmtLibTokenizer &t) {\n    unsigned num_s_exps_to_skip = 0;\n    if (t.peek() == \"(\") {\n        t.consume(\"(\");\n        num_s_exps_to_skip++;\n        if (t.peek() == \"as\") {\n            t.consume(\"as\");\n            if (t.peek() == \"(\") {\n                t.consume(\"(\");\n                num_s_exps_to_skip++;\n            }\n        }\n    }\n    Fn fn;\n    term_ptr term = fn(t);\n    for (unsigned i = 0; i < num_s_exps_to_skip; ++i) {\n        skip_rest_of_s_exp(t);\n    }\n    return term;\n}\n\n/* INSERT 1 */\nterm_ptr SmtLibParser::parse_term(SmtLibTokenizer &t, Symbol sym) const {\n    switch (sym) {\n/* INSERT 2 */\n    default: abort();\n    }\n}\n\n}"
  },
  {
    "path": "src/main/resources/codegen/src/smt_parser.hpp",
    "content": "//\n// Created by Aaron Bembenek on 1/23/23.\n//\n\n#ifndef CODEGEN_SMT_PARSER_HPP\n#define CODEGEN_SMT_PARSER_HPP\n\n#include <istream>\n#include <optional>\n#include \"Term.hpp\"\n\nnamespace flg {\n\nclass SmtLibTokenizer {\npublic:\n    explicit SmtLibTokenizer(std::istream &is) : m_is(is) {}\n\n    void ignore_whitespace(bool ignore) {\n        m_ignore_whitespace = ignore;\n    }\n\n    bool ignoring_whitespace() {\n        return m_ignore_whitespace;\n    }\n\n    const std::string &peek();\n\n    std::string next();\n\n    bool has_next();\n\n    void consume(const std::string &s);\n\nprivate:\n    std::istream &m_is;\n    bool m_ignore_whitespace{true};\n    std::optional<std::string> m_next{};\n\n    void load(bool allow_eof);\n\n    static bool is_word_char(int ch) {\n        return ch == '_' || std::isalnum(ch);\n    }\n};\n\nclass SmtLibParser {\npublic:\n    explicit SmtLibParser(std::unordered_map<std::string, term_ptr> &vars) : m_vars(vars) {}\n\n    Model get_model(std::istream &is) const;\n\nprivate:\n    const std::unordered_map<std::string, term_ptr> &m_vars;\n\n    void consume_comment(SmtLibTokenizer &t) const;\n\n    void parse_function_def(Model &m, SmtLibTokenizer &t) const;\n\n    void parse_type(SmtLibTokenizer &t) const;\n\n    bool should_record(Symbol sym) const;\n\n    term_ptr parse_term(SmtLibTokenizer &t, Symbol sym) const;\n};\n\n}\n\n#endif //CODEGEN_SMT_PARSER_HPP\n"
  },
  {
    "path": "src/main/resources/codegen/src/smt_shim.cpp",
    "content": "//\n// Created by Aaron Bembenek on 8/9/22.\n//\n\n#include \"globals.h\"\n#include \"smt_shim.h\"\n#include \"smt_parser.hpp\"\n#include <bitset>\n#include <boost/format.hpp>\n#include <chrono>\n#include <regex>\n\nnamespace flg::smt {\n\nstatic const auto declarations = R\"_(\n/* INSERT 0 */\n)_\";\n\nbool is_solver_var(term_ptr t) {\n    switch (t->sym) {\n/* INSERT 1 */\n        default:\n            return false;\n    }\n}\n\nbool needs_type_annotation(Symbol sym) {\n    switch (sym) {\n/* INSERT 2 */\n        default:\n            return false;\n    }\n}\n\nclass MyTypeInferer {\npublic:\n    std::deque<Type> go(term_ptr t);\n\nprivate:\n    std::deque<Type> m_annotations;\n    std::vector<std::pair<Type, Type>> m_constraints;\n    TypeSubst m_subst;\n\n    Type visit(term_ptr t);\n\n    void unify_constraints();\n\n    void unify(const Type &ty1, const Type &ty2);\n};\n\nstd::deque<Type> MyTypeInferer::go(term_ptr t) {\n    m_constraints.clear();\n    m_subst.clear();\n    m_annotations.clear();\n    visit(t);\n    unify_constraints();\n    for (auto &type: m_annotations) {\n        type = m_subst.apply(type);\n    }\n    return m_annotations;\n}\n\nType MyTypeInferer::visit(term_ptr t) {\n    std::vector<Type> types;\n    functor_type ft = Type::lookup(t->sym);\n    if (needs_type_annotation(t->sym)) {\n        m_annotations.push_back(ft.second);\n    }\n    if (!ft.first.empty()) {\n        auto &x = t->as_complex();\n        if (!is_solver_var(t)) {\n            for (size_t i = 0; i < x.arity; ++i) {\n                m_constraints.emplace_back(visit(x.val[i]), ft.first[i]);\n            }\n        }\n    }\n    return ft.second;\n}\n\nvoid MyTypeInferer::unify_constraints() {\n    while (!m_constraints.empty()) {\n        auto constraint = m_constraints.back();\n        m_constraints.pop_back();\n        auto ty1 = m_subst.apply(constraint.first);\n        auto ty2 = m_subst.apply(constraint.second);\n        if (ty1.is_var) {\n            m_subst.put(ty1, ty2);\n        } else if (ty2.is_var) {\n            m_subst.put(ty2, ty1);\n        } else {\n            unify(ty1, ty2);\n        }\n    }\n}\n\nvoid MyTypeInferer::unify(const Type &ty1, const Type &ty2) {\n    assert(ty1.name == ty2.name);\n    auto args1 = ty1.args;\n    auto args2 = ty2.args;\n    for (auto it1 = args1.begin(), it2 = args2.begin();\n         it1 != args1.end(); it1++, it2++) {\n        m_constraints.emplace_back(*it1, *it2);\n    }\n}\n\nSmtLibShim::SmtLibShim(boost::process::child &&proc, boost::process::opstream &&in, boost::process::ipstream &&out)\n        : m_proc{std::move(proc)}, m_in{std::move(in)}, m_out{std::move(out)} {\n            s_shims[this] = true;\n        }\n\nvoid SmtLibShim::set_timeout(int32_t timeout) {\n    if (timeout < 0) {\n        cerr << \"Warning: ignoring negative timeout provided to SMT\" << endl;\n        timeout = numeric_limits<int32_t>::max();\n    }\n    m_in << \"(set-option :timeout \" << timeout << \")\\n\";\n}\n\nvoid SmtLibShim::declare_vars(term_ptr t) {\n    if (is_solver_var(t)) {\n        if (m_solver_vars.find(t) == m_solver_vars.end()) {\n            std::stringstream ss;\n            ss << \"x\" << t->intize();\n            auto s = ss.str();\n            m_solver_vars.emplace(t, s);\n            m_solver_var_lookup.emplace(s, t);\n            m_solver_vars_in_order.push_back(t);\n            m_in << \"(declare-fun \" << s << \" () \" << Type::lookup(t->sym).second << \")\\n\";\n        }\n        return;\n    }\n    switch (t->sym) {\n        case Symbol::boxed_bool:\n        case Symbol::boxed_i32:\n        case Symbol::boxed_i64:\n        case Symbol::boxed_fp32:\n        case Symbol::boxed_fp64:\n        case Symbol::boxed_string:\n            break;\n        default:\n            auto &x = t->as_complex();\n            for (size_t i = 0; i < x.arity; ++i) {\n                declare_vars(x.val[i]);\n            }\n    }\n}\n\nvoid SmtLibShim::make_declarations() {\n    m_in << declarations << \"\\n\";\n}\n\nvoid SmtLibShim::push() {\n    // Avoid push timing out. See <https://github.com/Z3Prover/z3/issues/4713>\n    set_timeout(numeric_limits<int32_t>::max());\n    m_in << \"(push 1)\\n\";\n    m_stack_positions.push_back(m_solver_vars_in_order.size());\n}\n\nvoid SmtLibShim::pop(unsigned int n) {\n    m_in << \"(pop \" << n << \")\\n\";\n    if (n > 0) {\n        unsigned int stack_pos = m_stack_positions.size() - n;\n        unsigned int start_pos = m_stack_positions[stack_pos];\n        m_stack_positions.erase(m_stack_positions.begin() + stack_pos, m_stack_positions.end());\n        unsigned int how_many = m_solver_vars_in_order.size() - start_pos;\n        for (unsigned int i = 0; i < how_many; ++i) {\n            auto var = m_solver_vars_in_order.back();\n            auto symbol = m_solver_vars[var];\n            m_solver_vars.erase(var);\n            m_solver_var_lookup.erase(symbol);\n            m_solver_vars_in_order.pop_back();\n        }\n    }\n}\n\nvoid SmtLibShim::make_assertion(term_ptr assertion) {\n    declare_vars(assertion);\n    m_annotations = MyTypeInferer().go(assertion);\n    m_in << \"(assert \";\n    serialize(assertion);\n    m_in << \")\\n\";\n    assert(m_annotations.empty());\n}\n\nSmtStatus SmtLibShim::check_sat_assuming(const std::vector<term_ptr> &onVars, const std::vector<term_ptr> &offVars,\n                                         int timeout) {\n    set_timeout(timeout);\n    if (onVars.empty() && offVars.empty()) {\n        m_in << \"(check-sat)\\n\";\n    } else {\n        m_in << \"(check-sat-assuming (\";\n        for (auto var: onVars) {\n            m_in << lookup_var(var) << \" \";\n        }\n        for (auto var: offVars) {\n            m_in << \"(not \" << lookup_var(var) << \") \";\n        }\n        m_in << \"))\\n\";\n    }\n    m_in.flush();\n\n    string line;\n    auto op = [&] { getline(m_out, line); };\n    if (globals::smt_stats) {\n        globals::smt_time.local() += time(op);\n    } else {\n        op();\n    }\n    SmtStatus status;\n    if (line == \"sat\") {\n        status = SmtStatus::sat;\n    } else if (line == \"unsat\") {\n        status = SmtStatus::unsat;\n    } else if (line == \"unknown\") {\n        status = SmtStatus::unknown;\n    } else {\n        cerr << \"Unexpected SMT response:\" << endl;\n        cerr << line << endl;\n        abort();\n    }\n    return status;\n}\n\nModel SmtLibShim::get_model() {\n    m_in << \"(get-model)\\n\";\n    m_in.flush();\n    SmtLibParser parser(m_solver_var_lookup);\n    return parser.get_model(m_out);\n}\n\nvoid SmtLibShim::serialize(term_ptr t) {\n    switch (t->sym) {\n        case Symbol::boxed_bool: {\n            m_in << *t;\n            break;\n        }\n        case Symbol::boxed_string: {\n            m_in << \"\\\"\" << std::regex_replace(t->as_base<std::string>().val, std::regex(\"\\\"\"), \"\\\"\\\"\") << \"\\\"\";\n            break;\n        }\n        case Symbol::boxed_i32: {\n            m_in << \"#x\" << boost::format{\"%08x\"} % t->as_base<int32_t>().val;\n            break;\n        }\n        case Symbol::boxed_i64: {\n            m_in << \"#x\" << boost::format{\"%016x\"} % t->as_base<int64_t>().val;\n            break;\n        }\n        case Symbol::boxed_fp32: {\n            serialize_fp<float, 8, 24>(t);\n            break;\n        }\n        case Symbol::boxed_fp64: {\n            serialize_fp<double, 11, 53>(t);\n            break;\n        }\n/* INSERT 3 */\n        default:\n            auto &x = t->as_complex();\n            stringstream ss;\n            serialize(serialize_sym(x.sym), x);\n    }\n}\n\nvoid SmtLibShim::serialize(const std::string &repr, const ComplexTerm &t) {\n    size_t n = t.arity;\n    if (n > 0) {\n        m_in << \"(\";\n    }\n    if (needs_type_annotation(t.sym)) {\n        m_in << \"(as \" << repr << \" \" << next_annotation() << \")\";\n    } else {\n        m_in << repr;\n    }\n    for (size_t i = 0; i < n; ++i) {\n        m_in << \" \";\n        serialize(t.val[i]);\n    }\n    if (n > 0) {\n        m_in << \")\";\n    }\n}\n\ntemplate<typename T, size_t N>\nvoid SmtLibShim::serialize_bit_string(T val) {\n    m_in << \"#b\" << std::bitset<N>(val).to_string();\n}\n\ntemplate<size_t From, size_t To, bool Signed>\nvoid SmtLibShim::serialize_bv_to_bv(term_ptr t) {\n    auto arg = arg0(t);\n    if (From < To) {\n        m_in << \"((_ \" << (Signed ? \"sign\" : \"zero\") << \"_extend \"\n             << (To - From) << \") \";\n        serialize(arg);\n        m_in << \")\";\n    } else if (From > To) {\n        m_in << \"((_ extract \" << (To - 1) << \" 0) \";\n        serialize(arg);\n        m_in << \")\";\n    } else {\n        serialize(arg);\n    }\n}\n\nvoid SmtLibShim::serialize_bv_extract(term_ptr t) {\n    m_in << \"((_ extract \" << *argn(t, 2) << \" \" << *argn(t, 1) << \") \";\n    serialize(arg0(t));\n    m_in << \")\";\n}\n\ntemplate<size_t E, size_t S>\nvoid SmtLibShim::serialize_bv_to_fp(term_ptr t) {\n    m_in << \"((_ to_fp \" << E << \" \" << S << \") RNE \";\n    serialize(arg0(t));\n    m_in << \")\";\n}\n\ntemplate<typename T, size_t E, size_t S>\nvoid SmtLibShim::serialize_fp(term_ptr t) {\n    auto val = t->as_base<T>().val;\n    stringstream ss;\n    ss << E << \" \" << S;\n    auto s = ss.str();\n    if (isnan(val)) {\n        m_in << \"(_ NaN \" << s << \")\";\n    } else if (isinf(val)) {\n        if (val > 0) {\n            m_in << \"(_ +oo \" << s << \")\";\n        } else {\n            m_in << \"(_ -oo \" << s << \")\";\n        }\n    } else {\n        m_in << \"((_ to_fp \" << s << \") RNE \" << val << \")\";\n    }\n}\n\ntemplate<size_t N, bool Signed>\nvoid SmtLibShim::serialize_fp_to_bv(term_ptr t) {\n    m_in << \"((_ \" << (Signed ? \"fp.to_sbv\" : \"fp.to_ubv\") << \" \" << N << \") RNE \";\n    serialize(arg0(t));\n    m_in << \")\";\n}\n\ntemplate<size_t E, size_t S>\nvoid SmtLibShim::serialize_fp_to_fp(term_ptr t) {\n    m_in << \"((_ to_fp \" << E << \" \" << S << \") RNE \";\n    serialize(arg0(t));\n    m_in << \")\";\n}\n\nvoid SmtLibShim::serialize_let(term_ptr t) {\n    auto &x = t->as_complex();\n    m_in << \"(let ((\";\n    serialize(x.val[0]);\n    m_in << \" \";\n    serialize(x.val[1]);\n    m_in << \")) \";\n    serialize(x.val[2]);\n    m_in << \")\";\n}\n\ntemplate<typename T>\nvoid SmtLibShim::serialize_int(term_ptr t) {\n    m_in << arg0(t)->as_base<T>().val;\n}\n\ntemplate<size_t W>\nvoid SmtLibShim::serialize_int2bv(term_ptr t) {\n    m_in << \"((_ int2bv \" << W << \") \";\n    serialize(arg0(t));\n    m_in << \")\";\n}\n\ntemplate<bool Exists>\nvoid SmtLibShim::serialize_quantifier(term_ptr t) {\n    auto &x = t->as_complex();\n    m_in << \"(\" << (Exists ? \"exists (\" : \"forall (\");\n    for (auto &v: Term::vectorize_list_term(x.val[0])) {\n        // Consume annotation for cons\n        next_annotation();\n        auto var = arg0(v);\n        m_in << \"(\";\n        serialize(var);\n        m_in << \" \" << Type::lookup(var->sym).second << \")\";\n    }\n    m_in << \") \";\n    // Consume annotation for nil\n    next_annotation();\n    auto pats = Term::vectorize_list_term(x.val[2]);\n    if (!pats.empty()) {\n        m_in << \"(! \";\n    }\n    serialize(x.val[1]);\n    if (!pats.empty()) {\n        for (auto &pat: pats) {\n            m_in << \" :pattern (\";\n            // Consume annotation for cons\n            next_annotation();\n            bool first{true};\n            for (auto &sub: Term::vectorize_list_term(pat)) {\n                if (!first) {\n                    m_in << \" \";\n                }\n                first = false;\n                // Consume annotation for cons\n                next_annotation();\n                serialize(arg0(sub));\n            }\n            m_in << \")\";\n            // Consume annotation for nil\n            next_annotation();\n        }\n        m_in << \")\";\n    }\n    // Consume annotation for nil\n    next_annotation();\n    m_in << \")\";\n}\n\nstring SmtLibShim::serialize_sym(Symbol sym) {\n    switch (sym) {\n/* INSERT 4 */\n        default:\n            stringstream ss;\n            ss << \"|\" << sym << \"|\";\n            return ss.str();\n    }\n}\n\nstring SmtLibShim::serialize_tester(Symbol sym) {\n    stringstream ss;\n    ss << sym;\n    string s = ss.str().substr(4, string::npos);\n    return \"|is-\" + s + \"|\";\n}\n\n}"
  },
  {
    "path": "src/main/resources/codegen/src/smt_shim.h",
    "content": "//\n// Created by Aaron Bembenek on 8/9/22.\n//\n\n#ifndef CODEGEN_SMT_SHIM_H\n#define CODEGEN_SMT_SHIM_H\n\n#include <unordered_set>\n#include <boost/process.hpp>\n#include <tbb/concurrent_unordered_map.h>\n#include \"Term.hpp\"\n#include \"Type.hpp\"\n\nnamespace flg::smt {\n\nenum class SmtStatus {\n    sat, unsat, unknown\n};\n\nclass SmtShim {\npublic:\n    NO_COPY_OR_ASSIGN(SmtShim);\n\n    SmtShim() = default;\n\n    virtual void make_declarations() = 0;\n\n    virtual void make_assertion(term_ptr assertion) = 0;\n\n    virtual void push() = 0;\n\n    virtual void pop(unsigned int n) = 0;\n\n    void pop() { pop(1); }\n\n    virtual SmtStatus\n    check_sat_assuming(const std::vector<term_ptr> &onVars, const std::vector<term_ptr> &offVars, int32_t timeout) = 0;\n\n    virtual Model get_model() = 0;\n\n    virtual ~SmtShim() = default;\n};\n\nclass SmtLibShim : public SmtShim {\npublic:\n    NO_COPY_OR_ASSIGN(SmtLibShim);\n\n    SmtLibShim(boost::process::child &&proc, boost::process::opstream &&in, boost::process::ipstream &&out);\n\n    void make_declarations() override;\n\n    void make_assertion(term_ptr assertion) override;\n\n    void push() override;\n\n    void pop(unsigned int n) override;\n\n    SmtStatus\n    check_sat_assuming(const std::vector<term_ptr> &onVars, const std::vector<term_ptr> &offVars, int32_t timeout) override;\n\n    Model get_model() override;\n\n    static void terminate_all_children() {\n        for (auto p : s_shims) {\n            if (p.second) {\n                p.first->m_in.close();\n                p.first->m_proc.terminate();\n            }\n        }\n    }\n\n    ~SmtLibShim() override {\n        s_shims[this] = false;\n    }\n\nprivate:\n    inline static tbb::concurrent_unordered_map<SmtLibShim *, bool> s_shims;\n\n    class Logger {\n    public:\n        explicit Logger(boost::process::opstream &&in) : m_in{std::move(in)} {}\n\n        template<typename T>\n        Logger &operator<<(const T &val) {\n            m_in << val;\n            /*\n            std::cerr << val;\n            std::cerr.flush();\n            */\n            return *this;\n        }\n\n        void flush() {\n            m_in.flush();\n            /*\n            std::cerr.flush();\n            */\n        }\n\n        void close() {\n            m_in.close();\n        }\n\n    private:\n        boost::process::opstream m_in;\n    };\n\n    boost::process::child m_proc;\n    Logger m_in;\n    boost::process::ipstream m_out;\n\n    std::unordered_map<term_ptr, string> m_solver_vars;\n    std::unordered_map<string, term_ptr> m_solver_var_lookup;\n    std::vector<term_ptr> m_solver_vars_in_order;\n    std::vector<unsigned int> m_stack_positions;\n    std::deque<Type> m_annotations;\n\n    void set_timeout(int32_t timeout);\n\n    Type next_annotation() {\n        auto ty = m_annotations.front();\n        m_annotations.pop_front();\n        return ty;\n    }\n\n    void declare_vars(term_ptr t);\n\n    std::string lookup_var(term_ptr var) {\n        return m_solver_vars.find(var)->second;\n    }\n\n    void serialize(term_ptr t);\n\n    static term_ptr argn(term_ptr t, size_t n) {\n        return t->as_complex().val[n];\n    }\n\n    static term_ptr arg0(term_ptr t) {\n        return argn(t, 0);\n    }\n\n    void serialize(const std::string &repr, const ComplexTerm &t);\n\n    template<typename T, size_t N>\n    void serialize_bit_string(T val);\n\n    template<size_t From, size_t To, bool Signed>\n    void serialize_bv_to_bv(term_ptr t);\n\n    void serialize_bv_extract(term_ptr t);\n\n    template<size_t E, size_t S>\n    void serialize_bv_to_fp(term_ptr t);\n\n    template<typename T, size_t E, size_t S>\n    void serialize_fp(term_ptr t);\n\n    template<size_t N, bool Signed>\n    void serialize_fp_to_bv(term_ptr t);\n\n    template<size_t S, size_t E>\n    void serialize_fp_to_fp(term_ptr t);\n\n    void serialize_let(term_ptr t);\n\n    template<typename T>\n    void serialize_int(term_ptr t);\n\n    template<bool Exists>\n    void serialize_quantifier(term_ptr t);\n\n    std::string serialize_sym(Symbol sym);\n\n    std::string serialize_tester(Symbol sym);\n\n    template<size_t W>\n    void serialize_int2bv(term_ptr t);\n};\n\n}\n\n#endif //CODEGEN_SMT_SHIM_H\n"
  },
  {
    "path": "src/main/resources/codegen/src/smt_solver.cpp",
    "content": "//\n// Created by Aaron Bembenek on 8/9/22.\n//\n\n#include \"globals.h\"\n#include \"smt_solver.h\"\n\n#include <mutex>\n\nnamespace flg::smt {\n\nvoid break_into_conjuncts(term_ptr t, std::vector<term_ptr> &acc);\nvoid break_into_conjuncts_negated(term_ptr t, std::vector<term_ptr> &acc);\n\nstd::vector<term_ptr> break_into_conjuncts(term_ptr t) {\n    std::vector<term_ptr> conjuncts;\n    break_into_conjuncts(t, conjuncts);\n    return conjuncts;\n}\n\nTopLevelSmtSolver::TopLevelSmtSolver() {\n    std::unique_ptr<SmtSolver> inner;\n    switch (globals::smt_solver_mode) {\n        case SmtSolverMode::naive:\n            inner = std::make_unique<PushPopNaiveSolver>(make_shim());\n            break;\n        case SmtSolverMode::push_pop:\n            inner = std::make_unique<PushPopSolver>(make_shim());\n            break;\n        case SmtSolverMode::check_sat_assuming:\n            inner = std::make_unique<CheckSatAssumingSolver>(make_shim());\n            if (globals::smt_double_check) {\n                auto checker = std::make_unique<PushPopSolver>(make_shim());\n                inner = std::make_unique<DoubleCheckingSolver>(std::move(inner), std::move(checker));\n            }\n            break;\n    }\n    m_delegate = std::make_unique<MemoizingSmtSolver>(std::move(inner));\n}\n\nstd::unique_ptr<SmtShim> TopLevelSmtSolver::make_shim() {\n    namespace bp = boost::process;\n    // Force synchronization here to avoid some issues that come up with the\n    // `bp::child` constructor and pipes in a multithreaded setting. See\n    // <https://github.com/HarvardPL/formulog/issues/30>\n    static std::mutex mtx;\n    std::lock_guard<std::mutex> guard(mtx);\n    bp::ipstream out;\n    bp::opstream in;\n    bp::child proc(\"z3 -in\", bp::std_in < in, (bp::std_out & bp::std_err) > out);\n    return std::make_unique<SmtLibShim>(std::move(proc), std::move(in), std::move(out));\n}\n\nSmtResult TopLevelSmtSolver::check(const std::vector<term_ptr> &assertions, bool get_model, int timeout) {\n    return m_delegate->check(assertions, get_model, timeout);\n}\n\nSmtResult TopLevelSmtSolver::check(term_ptr assertion) {\n    return check(break_into_conjuncts(assertion), false, std::numeric_limits<int>::max());\n}\n\nMemoizingSmtSolver::MemoizingSmtSolver(std::unique_ptr<SmtSolver> &&delegate) : m_delegate{std::move(delegate)} {}\n\nSmtResult MemoizingSmtSolver::check(const std::vector<term_ptr> &assertions, bool get_model, int timeout) {\n    if (timeout < 0) {\n        timeout = -1;\n    }\n    if (assertions.empty()) {\n        auto model = get_model ? std::optional<Model>{} : std::nullopt;\n        return {SmtStatus::sat, model};\n    }\n    std::set<term_ptr> set(assertions.begin(), assertions.end());\n    if (set.find(Term::make(false)) != set.end()) {\n        return {SmtStatus::unsat, {}};\n    }\n    SmtResult res;\n    std::shared_future<SmtResult> fut;\n    memo_key_t key{std::move(set), get_model, timeout};\n    auto it = s_memo.find(key);\n    if (it == s_memo.end()) {\n        std::promise<SmtResult> p;\n        fut = p.get_future();\n        auto it2 = s_memo.emplace(std::move(key), fut);\n        if (it2.second) {\n            res = m_delegate->check(assertions, get_model, timeout);\n            p.set_value(res);\n        } else {\n            fut = it2.first->second;\n            auto op = [&] { res = fut.get(); };\n            if (globals::smt_stats) {\n                globals::smt_wait_time.local() += time(op);\n            } else {\n                op();\n            }\n        }\n    } else {\n        fut = it->second;\n        auto op = [&] { res = fut.get(); };\n        if (globals::smt_stats) {\n            globals::smt_wait_time.local() += time(op);\n        } else {\n            op();\n        }\n    }\n    return res;\n}\n\nSmtResult AbstractSmtSolver::check(const std::vector<term_ptr> &assertions, bool get_model, int timeout) {\n    if (!m_initialized) {\n        initialize();\n        m_initialized = true;\n    }\n    term_vector_pair p = make_assertions(assertions);\n    auto status = m_shim->check_sat_assuming(p.first, p.second, timeout);\n    SmtResult res{status, {}};\n    if (status == SmtStatus::sat && get_model) {\n        res.model = m_shim->get_model();\n    }\n    cleanup();\n    return res;\n}\n\nvoid PushPopNaiveSolver::initialize() {\n    m_shim->make_declarations();\n}\n\nAbstractSmtSolver::term_vector_pair PushPopNaiveSolver::make_assertions(const std::vector<term_ptr> &assertions) {\n    m_shim->push();\n    for (auto assertion: assertions) {\n        m_shim->make_assertion(assertion);\n    }\n    if (globals::smt_stats) {\n        globals::smt_cache_clears.local()++;\n        globals::smt_cache_misses.local() += assertions.size();\n    }\n    return {{},\n            {}};\n}\n\nvoid PushPopNaiveSolver::cleanup() {\n    m_shim->pop();\n}\n\nvoid CheckSatAssumingSolver::initialize() {\n    m_shim->make_declarations();\n    m_shim->push();\n}\n\nAbstractSmtSolver::term_vector_pair CheckSatAssumingSolver::make_assertions(const std::vector<term_ptr> &assertions) {\n    std::vector<term_ptr> on_vars;\n    unsigned hits{0};\n    unsigned misses{0};\n    for (auto assertion: assertions) {\n        auto &var = m_conjuncts_to_vars[assertion];\n        if (!var) {\n            var = ComplexTerm::fresh_smt_var();\n            misses++;\n        } else {\n            hits++;\n        }\n        on_vars.emplace_back(var);\n#ifdef FLG_DEV\n        auto imp = nullptr;\n#else\n        auto imp = Term::make<Symbol::smt_imp>(var, assertion);\n#endif\n        m_shim->make_assertion(imp);\n    }\n    if (globals::smt_stats) {\n        if (hits) {\n            globals::smt_cache_hits.local() += hits;\n        }\n        if (misses) {\n            globals::smt_cache_misses.local() += misses;\n        }\n    }\n    return {on_vars,\n            {}};\n}\n\nvoid CheckSatAssumingSolver::cleanup() {\n    if (m_conjuncts_to_vars.size() > globals::smt_cache_size) {\n        m_conjuncts_to_vars.clear();\n        m_shim->pop();\n        m_shim->push();\n        if (globals::smt_stats) {\n            globals::smt_cache_clears.local()++;\n        }\n    }\n}\n\nvoid PushPopSolver::initialize() {\n    m_shim->make_declarations();\n}\n\nAbstractSmtSolver::term_vector_pair PushPopSolver::make_assertions(const std::vector<term_ptr> &assertions) {\n    unsigned int pos = find_diff_pos(assertions);\n    if (globals::smt_stats) {\n        if (pos) {\n            globals::smt_cache_hits.local() += pos;\n        } else if (!m_stack.empty()) {\n            globals::smt_cache_clears.local()++;\n        }\n        auto misses = assertions.size() - pos;\n        if (misses) {\n            globals::smt_cache_misses.local() += misses;\n        }\n    }\n    shrink_cache(m_stack.size() - pos);\n    assert(m_stack.size() == pos);\n    assert(pos <= assertions.size());\n    for (auto it = assertions.begin() + pos; it != assertions.end(); ++it) {\n        auto elt = *it;\n        m_shim->push();\n        m_shim->make_assertion(elt);\n        m_stack.push_back(elt);\n    }\n    assert(m_stack.size() <= assertions.size());\n    return {{},\n            {}};\n}\n\nvoid PushPopSolver::cleanup() {\n    // do nothing\n}\n\nunsigned int PushPopSolver::find_diff_pos(const std::vector<term_ptr> &assertions) {\n    unsigned int pos = 0;\n    unsigned int end = std::min(m_stack.size(), assertions.size());\n    while (pos < end) {\n        if (m_stack[pos] != assertions[pos]) {\n            break;\n        }\n        pos++;\n    }\n    return pos;\n}\n\nvoid PushPopSolver::shrink_cache(unsigned int num_to_pop) {\n    m_shim->pop(num_to_pop);\n    for (unsigned int i = 0; i < num_to_pop; ++i) {\n        m_stack.pop_back();\n    }\n}\n\nSmtResult DoubleCheckingSolver::check(const std::vector<term_ptr> &assertions, bool get_model, int timeout) {\n    auto res = m_first->check(assertions, get_model, timeout);\n    if (res.status == SmtStatus::unknown) {\n        res = m_second->check(assertions, get_model, timeout);\n    }\n    return res;\n}\n\nvoid break_into_conjuncts_negated(term_ptr t, std::vector<term_ptr> &acc) {\n    if (t->sym == Symbol::smt_not) {\n        auto &c = t->as_complex();\n        // Turn ~~A into A\n        break_into_conjuncts(c.val[0], acc);\n    } else if (t->sym == Symbol::smt_imp) {\n        // Turn ~(A => B) into A /\\ ~B\n        auto &c = t->as_complex();\n        break_into_conjuncts(c.val[0], acc);\n        break_into_conjuncts_negated(c.val[1], acc);\n    } else if (t->sym == Symbol::smt_or) {\n        // Turn ~(A \\/ B) into ~A /\\ ~B\n        auto &c = t->as_complex();\n        break_into_conjuncts_negated(c.val[0], acc);\n        break_into_conjuncts_negated(c.val[1], acc);\n    } else if (t == Term::make(true)) {\n        // Turn ~True into False\n        acc.push_back(Term::make(false));\n    } else if (t != Term::make(false)) {\n#ifndef FLG_DEV\n        t = Term::make<Symbol::smt_not>(t);\n#endif\n        acc.push_back(t);\n    }\n}\n\nvoid break_into_conjuncts(term_ptr t, std::vector<term_ptr> &acc) {\n    if (t->sym == Symbol::smt_and) {\n        auto &c = t->as_complex();\n        break_into_conjuncts(c.val[0], acc);\n        break_into_conjuncts(c.val[1], acc);\n    } else if (t->sym == Symbol::smt_not) {\n        auto &c = t->as_complex();\n        break_into_conjuncts_negated(c.val[0], acc);\n    } else if (t != Term::make(true)) {\n        acc.push_back(t);\n    }\n}\n\n}"
  },
  {
    "path": "src/main/resources/codegen/src/smt_solver.h",
    "content": "//\n// Created by Aaron Bembenek on 8/9/22.\n//\n\n#ifndef CODEGEN_SMT_SOLVER_H\n#define CODEGEN_SMT_SOLVER_H\n\n#include \"smt_shim.h\"\n\n#include <future>\n#include <boost/container_hash/hash.hpp>\n#include <boost/program_options.hpp>\n\n#ifdef FLG_EAGER_EVAL\n#include <oneapi/tbb/enumerable_thread_specific.h>\n#endif\n\nnamespace flg::smt {\n\nenum class SmtSolverMode {\n    naive, push_pop, check_sat_assuming\n};\n\nstruct SmtResult {\n    SmtStatus status;\n    std::optional<Model> model;\n};\n\nclass SmtSolver {\npublic:\n    NO_COPY_OR_ASSIGN(SmtSolver);\n\n    SmtSolver() = default;\n\n    virtual SmtResult check(const std::vector<term_ptr> &assertions, bool get_model, int32_t timeout) = 0;\n\n    virtual ~SmtSolver() = default;\n};\n\nclass TopLevelSmtSolver : public SmtSolver {\npublic:\n    NO_COPY_OR_ASSIGN(TopLevelSmtSolver);\n\n    TopLevelSmtSolver();\n\n    SmtResult check(const std::vector<term_ptr> &assertions, bool get_model, int32_t timeout) override;\n\n    SmtResult check(term_ptr assertion);\n\nprivate:\n    std::unique_ptr<SmtSolver> m_delegate;\n\n    static std::unique_ptr<SmtShim> make_shim();\n};\n\n#ifdef FLG_EAGER_EVAL\nclass TBBTopLevelSmtSolver : public SmtSolver {\npublic:\n    NO_COPY_OR_ASSIGN(TBBTopLevelSmtSolver);\n\n    TBBTopLevelSmtSolver() = default;\n\n    SmtResult check(const std::vector<term_ptr> &assertions, bool get_model, int timeout) override {\n        return solvers.local().check(assertions, get_model, timeout);\n    }\n\n    SmtResult check(term_ptr assertion) {\n        return solvers.local().check(assertion);\n    }\n\nprivate:\n    oneapi::tbb::enumerable_thread_specific<TopLevelSmtSolver> solvers;\n};\n#endif\n\nclass MemoizingSmtSolver : public SmtSolver {\npublic:\n    NO_COPY_OR_ASSIGN(MemoizingSmtSolver);\n\n    explicit MemoizingSmtSolver(std::unique_ptr<SmtSolver> &&delegate);\n\n    SmtResult check(const std::vector<term_ptr> &assertions, bool get_model, int32_t timeout) override;\n\nprivate:\n    std::unique_ptr<SmtSolver> m_delegate;\n\n    typedef std::tuple<std::set<term_ptr>, bool, int32_t> memo_key_t;\n    static inline ConcurrentHashMap<memo_key_t, std::shared_future<SmtResult>, boost::hash<memo_key_t>> s_memo;\n};\n\nclass AbstractSmtSolver : public SmtSolver {\npublic:\n    NO_COPY_OR_ASSIGN(AbstractSmtSolver);\n\n    explicit AbstractSmtSolver(std::unique_ptr<SmtShim> &&shim) : m_shim{std::move(shim)} {}\n\n    using term_vector_pair = std::pair<std::vector<term_ptr>, std::vector<term_ptr>>;\n\n    SmtResult check(const std::vector<term_ptr> &assertions, bool get_model, int32_t timeout) override;\n\nprotected:\n    virtual void initialize() = 0;\n\n    virtual term_vector_pair make_assertions(const std::vector<term_ptr> &assertions) = 0;\n\n    virtual void cleanup() = 0;\n\n    std::unique_ptr<SmtShim> m_shim;\n\nprivate:\n    bool m_initialized{false};\n};\n\nclass PushPopNaiveSolver : public AbstractSmtSolver {\npublic:\n    NO_COPY_OR_ASSIGN(PushPopNaiveSolver);\n\n    explicit PushPopNaiveSolver(std::unique_ptr<SmtShim> &&shim) : AbstractSmtSolver{std::move(shim)} {}\n\nprivate:\n    void initialize() override;\n\n    term_vector_pair make_assertions(const std::vector<term_ptr> &assertions) override;\n\n    void cleanup() override;\n};\n\nclass CheckSatAssumingSolver : public AbstractSmtSolver {\npublic:\n    NO_COPY_OR_ASSIGN(CheckSatAssumingSolver);\n\n    explicit CheckSatAssumingSolver(std::unique_ptr<SmtShim> &&shim) : AbstractSmtSolver{std::move(shim)} {}\n\nprivate:\n    std::unordered_map<term_ptr, term_ptr> m_conjuncts_to_vars;\n\n    void initialize() override;\n\n    term_vector_pair make_assertions(const std::vector<term_ptr> &assertions) override;\n\n    void cleanup() override;\n};\n\nclass PushPopSolver : public AbstractSmtSolver {\npublic:\n    NO_COPY_OR_ASSIGN(PushPopSolver);\n\n    explicit PushPopSolver(std::unique_ptr<SmtShim> &&shim) : AbstractSmtSolver{std::move(shim)} {}\n\nprivate:\n    std::vector<term_ptr> m_stack;\n\n    void initialize() override;\n\n    term_vector_pair make_assertions(const std::vector<term_ptr> &assertions) override;\n\n    void cleanup() override;\n\n    unsigned int find_diff_pos(const std::vector<term_ptr> &assertions);\n\n    void shrink_cache(unsigned int num_to_pop);\n};\n\nclass DoubleCheckingSolver : public SmtSolver {\npublic:\n    NO_COPY_OR_ASSIGN(DoubleCheckingSolver);\n\n    DoubleCheckingSolver(std::unique_ptr<SmtSolver> &&first, std::unique_ptr<SmtSolver> &&second) : m_first(\n            std::move(first)), m_second(std::move(second)) {}\n\n    SmtResult check(const std::vector<term_ptr> &assertions, bool get_model, int32_t timeout) override;\n\nprivate:\n    std::unique_ptr<SmtSolver> m_first;\n    std::unique_ptr<SmtSolver> m_second;\n};\n\n#if FLG_EAGER_EVAL\ninline TBBTopLevelSmtSolver smt_solver;\n#else\nextern inline TopLevelSmtSolver smt_solver;\n#if defined(_OPENMP)\n#pragma omp threadprivate(smt_solver)\n#endif\nTopLevelSmtSolver smt_solver;\n#endif\n\n}\n\nnamespace std {\n\ninline std::istream &operator>>(std::istream &in, flg::smt::SmtSolverMode &mode) {\n    std::string token;\n    in >> token;\n\n    if (token == \"naive\") {\n        mode = flg::smt::SmtSolverMode::naive;\n    } else if (token == \"push-pop\") {\n        mode = flg::smt::SmtSolverMode::push_pop;\n    } else if (token == \"check-sat-assuming\") {\n        mode = flg::smt::SmtSolverMode::check_sat_assuming;\n    } else {\n        throw boost::program_options::validation_error(\n                boost::program_options::validation_error::kind_t::invalid_option_value);\n    }\n\n    return in;\n}\n\ninline std::ostream &operator<<(std::ostream &out, const flg::smt::SmtSolverMode &mode) {\n    switch (mode) {\n        case flg::smt::SmtSolverMode::naive:\n            out << \"naive\";\n            break;\n        case flg::smt::SmtSolverMode::push_pop:\n            out << \"push-pop\";\n            break;\n        case flg::smt::SmtSolverMode::check_sat_assuming:\n            out << \"check-sat-assuming\";\n            break;\n    }\n    return out;\n}\n\n}\n\n#endif //CODEGEN_SMT_SOLVER_H\n"
  },
  {
    "path": "src/main/resources/codegen/src/time.hpp",
    "content": "//\n// Created by Aaron Bembenek on 5/20/23.\n//\n\n#pragma once\n\n#include <chrono>\n\nnamespace flg {\n\ntypedef std::chrono::duration<double, std::milli> time_t;\n\ntemplate<typename F>\ntime_t time(const F &f) {\n    auto start = std::chrono::steady_clock::now();\n    f();\n    auto end = std::chrono::steady_clock::now();\n    return std::chrono::duration_cast<time_t>(end - start);\n}\n\n}\n"
  },
  {
    "path": "src/test/java/edu/harvard/seas/pl/formulog/codegen/CodeGenTester.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen;\n\nimport static org.junit.Assert.fail;\n\nimport edu.harvard.seas.pl.formulog.Configuration;\nimport edu.harvard.seas.pl.formulog.Main;\nimport edu.harvard.seas.pl.formulog.ast.BasicProgram;\nimport edu.harvard.seas.pl.formulog.eval.Tester;\nimport edu.harvard.seas.pl.formulog.magic.MagicSetTransformer;\nimport edu.harvard.seas.pl.formulog.parsing.Parser;\nimport edu.harvard.seas.pl.formulog.types.TypeChecker;\nimport edu.harvard.seas.pl.formulog.types.WellTypedProgram;\nimport edu.harvard.seas.pl.formulog.util.Util;\nimport java.io.BufferedReader;\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.io.PrintStream;\nimport java.net.URL;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\npublic class CodeGenTester implements Tester {\n\n  private List<String> inputDirs = Collections.emptyList();\n  private final boolean eagerEval;\n\n  public CodeGenTester(boolean eagerEval) {\n    this.eagerEval = eagerEval;\n  }\n\n  @Override\n  public void test(String file, List<String> inputDirs) {\n    boolean isBad = file.matches(\"test\\\\d\\\\d\\\\d_bd.flg\");\n    Path topPath = null;\n    try {\n      InputStream is = getClass().getClassLoader().getResourceAsStream(file);\n      if (is == null) {\n        throw new FileNotFoundException(file + \" not found\");\n      }\n      List<Path> dirs = new ArrayList<>();\n      for (String inputDir : inputDirs) {\n        URL dir = getClass().getClassLoader().getResource(inputDir);\n        dirs.add(Paths.get(dir.toURI()));\n      }\n      BasicProgram prog = new Parser().parse(new InputStreamReader(is), dirs);\n      WellTypedProgram wtp = new TypeChecker(prog).typeCheck();\n      MagicSetTransformer mst = new MagicSetTransformer(wtp);\n      BasicProgram magicProg = mst.transform(true, true);\n      String name = file.substring(0, file.indexOf('.'));\n      topPath = Path.of(\"codegen\", \"tests\", name);\n      boolean ok = evaluate(name, topPath, magicProg);\n      if (!ok && !isBad) {\n        String msg = \"Test failed for a good program\";\n        fail(msg);\n      }\n      if (ok && isBad) {\n        fail(\"Test succeeded for a bad program\");\n      }\n    } catch (Exception e) {\n      ByteArrayOutputStream baos = new ByteArrayOutputStream();\n      PrintStream out = new PrintStream(baos);\n      out.println(\"Unexpected exception:\");\n      e.printStackTrace(out);\n      fail(baos.toString());\n    } finally {\n      if (!Configuration.keepCodeGenTestDirs && topPath != null) {\n        Util.clean(topPath.toFile(), true);\n      }\n    }\n  }\n\n  private boolean evaluate(String name, Path topPath, BasicProgram prog) throws Exception {\n    File srcDir = topPath.resolve(\"src\").toFile();\n    srcDir.mkdirs();\n    Util.clean(srcDir, false);\n    Path buildPath = topPath.resolve(\"build\");\n    CodeGen cg = new CodeGen(prog, topPath.toFile());\n    cg.go();\n    var cmakeCmds =\n        new ArrayList<>(\n            Arrays.asList(\n                \"cmake\",\n                \"-B\",\n                buildPath.toString(),\n                \"-S\",\n                topPath.toString(),\n                \"-DCMAKE_BUILD_TYPE=Debug\"));\n    if (eagerEval) {\n      cmakeCmds.add(\"-DFLG_EAGER_EVAL=On\");\n    }\n    if (Configuration.cxxCompiler != null) {\n      cmakeCmds.add(\"-DCMAKE_CXX_COMPILER=\" + Configuration.cxxCompiler);\n    }\n    Process cmake = Runtime.getRuntime().exec(cmakeCmds.toArray(new String[0]));\n    if (cmake.waitFor() != 0) {\n      System.err.println(\"Could not cmake test\");\n      printToStdErr(cmake.getErrorStream());\n      return false;\n    }\n\n    Process make =\n        Runtime.getRuntime()\n            .exec(\n                new String[] {\n                  \"cmake\", \"--build\", buildPath.toString(), \"-j\", String.valueOf(Main.parallelism)\n                });\n    if (make.waitFor() != 0) {\n      System.err.println(\"Could not make test\");\n      printToStdErr(make.getErrorStream());\n      return false;\n    }\n\n    String cmd = topPath.resolve(\"build\").resolve(\"flg\").toString();\n    for (String inputDir : inputDirs) {\n      Path p = Paths.get(getClass().getClassLoader().getResource(inputDir).toURI());\n      cmd += \" --fact-dir \" + p;\n    }\n    cmd += \" --dump-sizes -j 4\";\n\n    Process flg = Runtime.getRuntime().exec(cmd);\n    if (flg.waitFor() != 0) {\n      System.err.println(\"Evaluation error\");\n      printToStdErr(flg.getErrorStream());\n      return false;\n    }\n\n    BufferedReader br = new BufferedReader(new InputStreamReader(flg.getInputStream()));\n    String line;\n    while ((line = br.readLine()) != null) {\n      if (line.equals(\"ok: 1\") || line.equals(\"query__ok: 1\")) {\n        return true;\n      }\n    }\n    return false;\n  }\n\n  private static void printToStdErr(InputStream is) throws IOException {\n    BufferedReader br = new BufferedReader(new InputStreamReader(is));\n    String line;\n    while ((line = br.readLine()) != null) {\n      System.err.println(line);\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/java/edu/harvard/seas/pl/formulog/codegen/CompiledEagerEvaluationTest.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2024 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen;\n\n/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2018 - 2020 President and Fellows of Harvard College\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 * #L%\n */\n\nimport edu.harvard.seas.pl.formulog.Configuration;\nimport edu.harvard.seas.pl.formulog.eval.CommonEvaluationTest;\nimport edu.harvard.seas.pl.formulog.eval.SemiNaiveEvaluation;\n\npublic class CompiledEagerEvaluationTest extends CommonEvaluationTest<SemiNaiveEvaluation> {\n\n  static {\n    if (!Configuration.testCodeGenEager) {\n      System.err.println(\n          \"WARNING: skipping CompiledEagerEvaluationTest; enable with system property\"\n              + \" `-DtestCodeGenEager`\");\n    }\n  }\n\n  public CompiledEagerEvaluationTest() {\n    super(Configuration.testCodeGenEager ? new CodeGenTester(true) : new NopTester<>());\n  }\n}\n"
  },
  {
    "path": "src/test/java/edu/harvard/seas/pl/formulog/codegen/CompiledEagerMagicSetTest.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2024 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen;\n\n/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2018 - 2020 President and Fellows of Harvard College\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 * #L%\n */\n\nimport edu.harvard.seas.pl.formulog.Configuration;\nimport edu.harvard.seas.pl.formulog.eval.SemiNaiveEvaluation;\nimport edu.harvard.seas.pl.formulog.magic.CommonMagicSetTest;\n\npublic class CompiledEagerMagicSetTest extends CommonMagicSetTest<SemiNaiveEvaluation> {\n\n  static {\n    if (!Configuration.testCodeGenEager) {\n      System.err.println(\n          \"WARNING: skipping CompiledEagerMagicSetTest; enable with system property\"\n              + \" `-DtestCodeGenEager`\");\n    }\n  }\n\n  public CompiledEagerMagicSetTest() {\n    super(Configuration.testCodeGenEager ? new CodeGenTester(true) : new NopTester<>());\n  }\n}\n"
  },
  {
    "path": "src/test/java/edu/harvard/seas/pl/formulog/codegen/CompiledSemiNaiveEvaluationTest.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen;\n\nimport edu.harvard.seas.pl.formulog.Configuration;\nimport edu.harvard.seas.pl.formulog.eval.CommonEvaluationTest;\nimport edu.harvard.seas.pl.formulog.eval.SemiNaiveEvaluation;\n\npublic class CompiledSemiNaiveEvaluationTest extends CommonEvaluationTest<SemiNaiveEvaluation> {\n\n  static {\n    if (!Configuration.testCodeGen) {\n      System.err.println(\n          \"WARNING: skipping CompiledSemiNaiveEvaluationTest; enable with system property\"\n              + \" `-DtestCodeGen`\");\n    }\n  }\n\n  public CompiledSemiNaiveEvaluationTest() {\n    super(Configuration.testCodeGen ? new CodeGenTester(false) : new NopTester<>());\n  }\n}\n"
  },
  {
    "path": "src/test/java/edu/harvard/seas/pl/formulog/codegen/CompiledSemiNaiveMagicSetTest.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen;\n\nimport edu.harvard.seas.pl.formulog.Configuration;\nimport edu.harvard.seas.pl.formulog.eval.SemiNaiveEvaluation;\nimport edu.harvard.seas.pl.formulog.magic.CommonMagicSetTest;\n\npublic class CompiledSemiNaiveMagicSetTest extends CommonMagicSetTest<SemiNaiveEvaluation> {\n\n  static {\n    if (!Configuration.testCodeGen) {\n      System.err.println(\n          \"WARNING: skipping CompiledSemiNaiveMagicSetTest; enable with system property\"\n              + \" `-DtestCodeGen`\");\n    }\n  }\n\n  public CompiledSemiNaiveMagicSetTest() {\n    super(Configuration.testCodeGen ? new CodeGenTester(false) : new NopTester<>());\n  }\n}\n"
  },
  {
    "path": "src/test/java/edu/harvard/seas/pl/formulog/codegen/NopTester.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.codegen;\n\nimport edu.harvard.seas.pl.formulog.eval.Evaluation;\nimport edu.harvard.seas.pl.formulog.eval.Tester;\nimport java.util.List;\n\npublic class NopTester<T extends Evaluation> implements Tester {\n\n  @Override\n  public void test(String file, List<String> inputDirs) {\n    // do nothing\n  }\n}\n"
  },
  {
    "path": "src/test/java/edu/harvard/seas/pl/formulog/eval/AbstractEvaluationTest.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.eval;\n\nimport java.util.Collections;\nimport java.util.List;\n\npublic abstract class AbstractEvaluationTest<T extends Evaluation> {\n\n  private final Tester tester;\n\n  public AbstractEvaluationTest(Tester tester) {\n    this.tester = tester;\n  }\n\n  protected void test(String file, List<String> inputDirs) {\n    tester.test(file, inputDirs);\n  }\n\n  protected void test(String file) {\n    test(file, Collections.singletonList(\"\"));\n  }\n}\n"
  },
  {
    "path": "src/test/java/edu/harvard/seas/pl/formulog/eval/AbstractTester.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.eval;\n\nimport static org.junit.Assert.fail;\n\nimport edu.harvard.seas.pl.formulog.ast.BasicProgram;\nimport edu.harvard.seas.pl.formulog.parsing.Parser;\nimport edu.harvard.seas.pl.formulog.types.TypeChecker;\nimport edu.harvard.seas.pl.formulog.types.WellTypedProgram;\nimport edu.harvard.seas.pl.formulog.validating.InvalidProgramException;\nimport java.io.ByteArrayOutputStream;\nimport java.io.FileNotFoundException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.io.PrintStream;\nimport java.net.URL;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic abstract class AbstractTester<T extends Evaluation> implements Tester {\n\n  @Override\n  public void test(String file, List<String> inputDirs) {\n    boolean isBad = file.matches(\"test\\\\d\\\\d\\\\d_bd.flg\");\n    try {\n      InputStream is = getClass().getClassLoader().getResourceAsStream(file);\n      if (is == null) {\n        throw new FileNotFoundException(file + \" not found\");\n      }\n      List<Path> dirs = new ArrayList<>();\n      for (String inputDir : inputDirs) {\n        URL dir = getClass().getClassLoader().getResource(inputDir);\n        dirs.add(Paths.get(dir.toURI()));\n      }\n      BasicProgram prog = new Parser().parse(new InputStreamReader(is), dirs);\n      WellTypedProgram wellTypedProg = (new TypeChecker(prog)).typeCheck();\n      T eval = setup(wellTypedProg);\n      boolean ok = evaluate(eval);\n      if (!ok && !isBad) {\n        String msg = \"Test failed for a good program\";\n        fail(msg);\n      }\n      if (ok && isBad) {\n        fail(\"Test succeeded for a bad program\");\n      }\n    } catch (Exception e) {\n      ByteArrayOutputStream baos = new ByteArrayOutputStream();\n      PrintStream out = new PrintStream(baos);\n      out.println(\"Unexpected exception:\");\n      e.printStackTrace(out);\n      fail(baos.toString());\n    }\n  }\n\n  protected abstract T setup(WellTypedProgram prog)\n      throws InvalidProgramException, EvaluationException;\n\n  protected abstract boolean evaluate(T eval) throws EvaluationException;\n}\n"
  },
  {
    "path": "src/test/java/edu/harvard/seas/pl/formulog/eval/CommonEvaluationTest.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2024 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.eval;\n\nimport java.util.Arrays;\nimport org.junit.Test;\n\npublic abstract class CommonEvaluationTest<T extends Evaluation> extends AbstractEvaluationTest<T> {\n\n  public CommonEvaluationTest(Tester tester) {\n    super(tester);\n  }\n\n  @Test\n  public void test018() {\n    test(\"test018_ok.flg\");\n  }\n\n  @Test\n  public void test019() {\n    test(\"test019_ok.flg\");\n  }\n\n  @Test\n  public void test020() {\n    test(\"test020_ok.flg\");\n  }\n\n  @Test\n  public void test021() {\n    test(\"test021_ok.flg\");\n  }\n\n  @Test\n  public void test022() {\n    test(\"test022_ok.flg\");\n  }\n\n  @Test\n  public void test023() {\n    test(\"test023_ok.flg\");\n  }\n\n  @Test\n  public void test024() {\n    test(\"test024_ok.flg\");\n  }\n\n  @Test\n  public void test027() {\n    test(\"test027_ok.flg\");\n  }\n\n  @Test\n  public void test029() {\n    test(\"test029_ok.flg\");\n  }\n\n  @Test\n  public void test030() {\n    test(\"test030_ok.flg\");\n  }\n\n  @Test\n  public void test032() {\n    test(\"test032_ok.flg\");\n  }\n\n  @Test\n  public void test033() {\n    test(\"test033_ok.flg\");\n  }\n\n  @Test\n  public void test034() {\n    test(\"test034_ok.flg\");\n  }\n\n  @Test\n  public void test035() {\n    test(\"test035_ok.flg\");\n  }\n\n  @Test\n  public void test037() {\n    test(\"test037_ok.flg\");\n  }\n\n  @Test\n  public void test038() {\n    test(\"test038_ok.flg\");\n  }\n\n  @Test\n  public void test039() {\n    test(\"test039_ok.flg\");\n  }\n\n  @Test\n  public void test040() {\n    test(\"test040_ok.flg\");\n  }\n\n  @Test\n  public void test041() {\n    test(\"test041_ok.flg\");\n  }\n\n  @Test\n  public void test043() {\n    test(\"test043_ok.flg\");\n  }\n\n  @Test\n  public void test044() {\n    test(\"test044_ok.flg\");\n  }\n\n  @Test\n  public void test045() {\n    test(\"test045_ok.flg\");\n  }\n\n  @Test\n  public void test046() {\n    test(\"test046_ok.flg\");\n  }\n\n  @Test\n  public void test047() {\n    test(\"test047_ok.flg\");\n  }\n\n  @Test\n  public void test048() {\n    test(\"test048_ok.flg\");\n  }\n\n  @Test\n  public void test056() {\n    test(\"test056_ok.flg\");\n  }\n\n  @Test\n  public void test057() {\n    test(\"test057_ok.flg\");\n  }\n\n  @Test\n  public void test058() {\n    test(\"test058_ok.flg\");\n  }\n\n  @Test\n  public void test061() {\n    test(\"test061_ok.flg\");\n  }\n\n  @Test\n  public void test062() {\n    test(\"test062_ok.flg\");\n  }\n\n  @Test\n  public void test063() {\n    test(\"test063_ok.flg\");\n  }\n\n  @Test\n  public void test064() {\n    test(\"test064_ok.flg\");\n  }\n\n  @Test\n  public void test065() {\n    test(\"test065_ok.flg\");\n  }\n\n  @Test\n  public void test066() {\n    test(\"test066_ok.flg\");\n  }\n\n  @Test\n  public void test067() {\n    test(\"test067_ok.flg\");\n  }\n\n  @Test\n  public void test068() {\n    test(\"test068_ok.flg\");\n  }\n\n  @Test\n  public void test069() {\n    test(\"test069_ok.flg\");\n  }\n\n  @Test\n  public void test070() {\n    test(\"test070_ok.flg\");\n  }\n\n  @Test\n  public void test071() {\n    test(\"test071_ok.flg\");\n  }\n\n  @Test\n  public void test072() {\n    test(\"test072_ok.flg\");\n  }\n\n  @Test\n  public void test073() {\n    test(\"test073_ok.flg\");\n  }\n\n  @Test\n  public void test077() {\n    test(\"test077_ok.flg\");\n  }\n\n  @Test\n  public void test078() {\n    test(\"test078_ok.flg\");\n  }\n\n  @Test\n  public void test079() {\n    test(\"test079_ok.flg\");\n  }\n\n  @Test\n  public void test081() {\n    test(\"test081_ok.flg\");\n  }\n\n  @Test\n  public void test082() {\n    test(\"test082_ok.flg\");\n  }\n\n  @Test\n  public void test083() {\n    test(\"test083_ok.flg\");\n  }\n\n  @Test\n  public void test084() {\n    test(\"test084_ok.flg\");\n  }\n\n  @Test\n  public void test085() {\n    test(\"test085_ok.flg\");\n  }\n\n  @Test\n  public void test086() {\n    test(\"test086_ok.flg\");\n  }\n\n  @Test\n  public void test087() {\n    test(\"test087_ok.flg\");\n  }\n\n  @Test\n  public void test088() {\n    test(\"test088_ok.flg\");\n  }\n\n  @Test\n  public void test089() {\n    test(\"test089_ok.flg\");\n  }\n\n  @Test\n  public void test090() {\n    test(\"test090_ok.flg\");\n  }\n\n  @Test\n  public void test092() {\n    test(\"test092_ok.flg\");\n  }\n\n  @Test\n  public void test093() {\n    test(\"test093_ok.flg\");\n  }\n\n  @Test\n  public void test094() {\n    test(\"test094_ok.flg\");\n  }\n\n  @Test\n  public void test095() {\n    test(\"test095_ok.flg\");\n  }\n\n  @Test\n  public void test096() {\n    test(\"test096_ok.flg\");\n  }\n\n  @Test\n  public void test097() {\n    test(\"test097_ok.flg\");\n  }\n\n  @Test\n  public void test099() {\n    test(\"test099_ok.flg\");\n  }\n\n  @Test\n  public void test100() {\n    test(\"test100_ok.flg\");\n  }\n\n  @Test\n  public void test102() {\n    test(\"test102_ok.flg\");\n  }\n\n  @Test\n  public void test103() {\n    test(\"test103_ok.flg\");\n  }\n\n  @Test\n  public void test104() {\n    test(\"test104_ok.flg\");\n  }\n\n  @Test\n  public void test105() {\n    test(\"test105_ok.flg\");\n  }\n\n  @Test\n  public void test106() {\n    test(\"test106_ok.flg\");\n  }\n\n  @Test\n  public void test107() {\n    test(\"test107_ok.flg\");\n  }\n\n  @Test\n  public void test108() {\n    test(\"test108_ok.flg\");\n  }\n\n  @Test\n  public void test109() {\n    test(\"test109_ok.flg\");\n  }\n\n  @Test\n  public void test110() {\n    test(\"test110_ok.flg\");\n  }\n\n  @Test\n  public void test111() {\n    test(\"test111_ok.flg\");\n  }\n\n  @Test\n  public void test112() {\n    test(\"test112_ok.flg\");\n  }\n\n  @Test\n  public void test113() {\n    test(\"test113_ok.flg\");\n  }\n\n  @Test\n  public void test114() {\n    test(\"test114_ok.flg\");\n  }\n\n  @Test\n  public void test115() {\n    test(\"test115_ok.flg\");\n  }\n\n  @Test\n  public void test116() {\n    test(\"test116_ok.flg\");\n  }\n\n  @Test\n  public void test117() {\n    test(\"test117_ok.flg\");\n  }\n\n  @Test\n  public void test119() {\n    test(\"test119_ok.flg\");\n  }\n\n  @Test\n  public void test120() {\n    test(\"test120_ok.flg\");\n  }\n\n  @Test\n  public void test121() {\n    test(\"test121_ok.flg\");\n  }\n\n  @Test\n  public void test122() {\n    test(\"test122_ok.flg\");\n  }\n\n  @Test\n  public void test123() {\n    test(\"test123_ok.flg\");\n  }\n\n  @Test\n  public void test124() {\n    test(\"test124_ok.flg\");\n  }\n\n  @Test\n  public void test125() {\n    test(\"test125_ok.flg\");\n  }\n\n  @Test\n  public void test126() {\n    test(\"test126_ok.flg\");\n  }\n\n  @Test\n  public void test127() {\n    test(\"test127_ok.flg\");\n  }\n\n  @Test\n  public void test128() {\n    test(\"test128_ok.flg\");\n  }\n\n  @Test\n  public void test129() {\n    test(\"test129_ok.flg\");\n  }\n\n  @Test\n  public void test134() {\n    test(\"test134_ok.flg\");\n  }\n\n  @Test\n  public void test135() {\n    test(\"test135_ok.flg\");\n  }\n\n  @Test\n  public void test136() {\n    test(\"test136_ok.flg\");\n  }\n\n  @Test\n  public void test137() {\n    test(\"test137_ok.flg\");\n  }\n\n  @Test\n  public void test139() {\n    test(\"test139_ok.flg\");\n  }\n\n  @Test\n  public void test180() {\n    test(\"test180_ok.flg\");\n  }\n\n  @Test\n  public void test181() {\n    test(\"test181_ok.flg\");\n  }\n\n  @Test\n  public void test182() {\n    test(\"test182_ok.flg\");\n  }\n\n  @Test\n  public void test183() {\n    test(\"test183_ok.flg\");\n  }\n\n  @Test\n  public void test184() {\n    test(\"test184_ok.flg\");\n  }\n\n  @Test\n  public void test186() {\n    test(\"test186_ok.flg\");\n  }\n\n  @Test\n  public void test187() {\n    test(\"test187_ok.flg\");\n  }\n\n  @Test\n  public void test189() {\n    test(\"test189_ok.flg\");\n  }\n\n  @Test\n  public void test190() {\n    test(\"test190_ok.flg\");\n  }\n\n  @Test\n  public void test191() {\n    test(\"test191_ok.flg\", Arrays.asList(\"test191_input\"));\n  }\n\n  @Test\n  public void test192() {\n    test(\"test192_ok.flg\");\n  }\n\n  @Test\n  public void test193() {\n    test(\"test193_ok.flg\");\n  }\n\n  @Test\n  public void test238() {\n    test(\"test238_ok.flg\");\n  }\n\n  @Test\n  public void test240() {\n    test(\"test240_ok.flg\");\n  }\n\n  @Test\n  public void test241() {\n    test(\"test241_ok.flg\");\n  }\n\n  @Test\n  public void test242() {\n    test(\"test242_ok.flg\");\n  }\n\n  @Test\n  public void test243() {\n    test(\"test243_ok.flg\");\n  }\n\n  @Test\n  public void test244() {\n    test(\"test244_ok.flg\");\n  }\n\n  @Test\n  public void test245() {\n    test(\"test245_ok.flg\");\n  }\n\n  @Test\n  public void test254() {\n    test(\"test254_ok.flg\");\n  }\n\n  @Test\n  public void test256() {\n    test(\"test256_ok.flg\");\n  }\n\n  @Test\n  public void test258() {\n    test(\"test258_ok.flg\");\n  }\n\n  @Test\n  public void test259() {\n    test(\"test259_ok.flg\");\n  }\n\n  @Test\n  public void test260() {\n    test(\"test260_ok.flg\");\n  }\n\n  @Test\n  public void test261() {\n    test(\"test261_ok.flg\");\n  }\n\n  @Test\n  public void test262() {\n    test(\"test262_ok.flg\");\n  }\n\n  @Test\n  public void test263() {\n    test(\"test263_ok.flg\");\n  }\n\n  @Test\n  public void test264() {\n    test(\"test264_ok.flg\");\n  }\n\n  @Test\n  public void test265() {\n    test(\"test265_ok.flg\");\n  }\n\n  @Test\n  public void test267() {\n    test(\"test267_ok.flg\");\n  }\n\n  @Test\n  public void test273() {\n    test(\"test273_ok.flg\");\n  }\n\n  @Test\n  public void test274() {\n    test(\"test274_ok.flg\");\n  }\n\n  @Test\n  public void test275() {\n    test(\"test275_ok.flg\");\n  }\n\n  @Test\n  public void test276() {\n    test(\"test276_ok.flg\", Arrays.asList(\"test276_inputA\", \"test276_inputB\"));\n  }\n\n  @Test\n  public void test277() {\n    test(\"test277_ok.flg\");\n  }\n\n  @Test\n  public void test278() {\n    test(\"test278_ok.flg\");\n  }\n\n  @Test\n  public void test279() {\n    test(\"test279_ok.flg\");\n  }\n\n  @Test\n  public void test280() {\n    test(\"test280_ok.flg\");\n  }\n\n  @Test\n  public void test281() {\n    test(\"test281_ok.flg\");\n  }\n\n  @Test\n  public void test282() {\n    test(\"test282_ok.flg\");\n  }\n\n  @Test\n  public void test283() {\n    test(\"test283_ok.flg\");\n  }\n\n  @Test\n  public void test284() {\n    test(\"test284_ok.flg\");\n  }\n\n  @Test\n  public void test285() {\n    test(\"test285_ok.flg\");\n  }\n\n  @Test\n  public void test286() {\n    test(\"test286_ok.flg\");\n  }\n\n  @Test\n  public void test287() {\n    test(\"test287_ok.flg\");\n  }\n\n  @Test\n  public void test288() {\n    test(\"test288_ok.flg\");\n  }\n\n  @Test\n  public void test289() {\n    test(\"test289_ok.flg\");\n  }\n\n  @Test\n  public void test290() {\n    test(\"test290_ok.flg\");\n  }\n\n  @Test\n  public void test291() {\n    test(\"test291_ok.flg\");\n  }\n\n  @Test\n  public void test292() {\n    test(\"test292_ok.flg\");\n  }\n\n  @Test\n  public void test297() {\n    test(\"test297_ok.flg\");\n  }\n\n  @Test\n  public void test298() {\n    test(\"test298_ok.flg\");\n  }\n\n  @Test\n  public void test299() {\n    test(\"test299_ok.flg\");\n  }\n\n  @Test\n  public void test300() {\n    test(\"test300_ok.flg\");\n  }\n\n  @Test\n  public void test301() {\n    test(\"test301_ok.flg\");\n  }\n\n  @Test\n  public void test304() {\n    test(\"test304_ok.flg\");\n  }\n\n  @Test\n  public void test305() {\n    test(\"test305_ok.flg\");\n  }\n\n  @Test\n  public void test306() {\n    test(\"test306_ok.flg\");\n  }\n\n  @Test\n  public void test308() {\n    test(\"test308_ok.flg\");\n  }\n\n  @Test\n  public void test309() {\n    test(\"test309_ok.flg\");\n  }\n\n  @Test\n  public void test310() {\n    test(\"test310_ok.flg\");\n  }\n\n  @Test\n  public void test311() {\n    test(\"test311_ok.flg\");\n  }\n\n  /*\n   * No longer supporting these semantics\n   *\n   * @Test public void test316() { test(\"test316_ok.flg\"); }\n   *\n   * @Test public void test317() { test(\"test317_ok.flg\"); }\n   *\n   * @Test public void test318() { test(\"test318_ok.flg\"); }\n   *\n   * @Test public void test319() { test(\"test319_ok.flg\"); }\n   */\n\n  @Test\n  public void test320() {\n    test(\"test320_ok.flg\");\n  }\n\n  @Test\n  public void test321() {\n    test(\"test321_ok.flg\");\n  }\n\n  @Test\n  public void test323() {\n    test(\"test323_ok.flg\");\n  }\n\n  @Test\n  public void test324() {\n    test(\"test324_ok.flg\");\n  }\n\n  @Test\n  public void test325() {\n    test(\"test325_ok.flg\");\n  }\n\n  @Test\n  public void test326() {\n    test(\"test326_ok.flg\");\n  }\n\n  @Test\n  public void test328() {\n    test(\"test328_ok.flg\");\n  }\n\n  @Test\n  public void test329() {\n    test(\"test329_ok.flg\");\n  }\n\n  @Test\n  public void test330() {\n    test(\"test330_ok.flg\");\n  }\n\n  @Test\n  public void test331() {\n    test(\"test331_ok.flg\");\n  }\n\n  @Test\n  public void test332() {\n    test(\"test332_ok.flg\");\n  }\n\n  @Test\n  public void test334() {\n    test(\"test334_ok.flg\");\n  }\n\n  @Test\n  public void test336() {\n    test(\"test336_ok.flg\");\n  }\n\n  @Test\n  public void test337() {\n    test(\"test337_ok.flg\");\n  }\n\n  @Test\n  public void test338() {\n    test(\"test338_ok.flg\");\n  }\n\n  @Test\n  public void test339() {\n    test(\"test339_ok.flg\");\n  }\n\n  @Test\n  public void test340() {\n    test(\"test340_ok.flg\");\n  }\n\n  @Test\n  public void test345() {\n    test(\"test345_ok.flg\");\n  }\n}\n"
  },
  {
    "path": "src/test/java/edu/harvard/seas/pl/formulog/eval/EagerSemiNaiveEvaluationTest.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.eval;\n\npublic class EagerSemiNaiveEvaluationTest extends CommonEvaluationTest<SemiNaiveEvaluation> {\n\n  public EagerSemiNaiveEvaluationTest() {\n    super(new InterpretedSemiNaiveTester(true));\n  }\n}\n"
  },
  {
    "path": "src/test/java/edu/harvard/seas/pl/formulog/eval/InterpretedSemiNaiveTester.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.eval;\n\nimport edu.harvard.seas.pl.formulog.symbols.RelationSymbol;\nimport edu.harvard.seas.pl.formulog.types.WellTypedProgram;\nimport edu.harvard.seas.pl.formulog.validating.InvalidProgramException;\n\npublic class InterpretedSemiNaiveTester extends AbstractTester<SemiNaiveEvaluation> {\n\n  private final boolean eagerEval;\n\n  public InterpretedSemiNaiveTester(boolean eagerEval) {\n    this.eagerEval = eagerEval;\n  }\n\n  @Override\n  protected SemiNaiveEvaluation setup(WellTypedProgram prog)\n      throws InvalidProgramException, EvaluationException {\n    return SemiNaiveEvaluation.setup(prog, 2, eagerEval);\n  }\n\n  @Override\n  protected boolean evaluate(SemiNaiveEvaluation eval) throws EvaluationException {\n    eval.run();\n    EvaluationResult res = eval.getResult();\n    RelationSymbol sym;\n    if (eval.hasQuery()) {\n      sym = eval.getQuery().getSymbol();\n    } else {\n      sym = (RelationSymbol) eval.getInputProgram().getSymbolManager().lookupSymbol(\"ok\");\n    }\n    return res.getAll(sym).iterator().hasNext();\n  }\n}\n"
  },
  {
    "path": "src/test/java/edu/harvard/seas/pl/formulog/eval/SemiNaiveEvaluationTest.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.eval;\n\npublic class SemiNaiveEvaluationTest extends CommonEvaluationTest<SemiNaiveEvaluation> {\n\n  public SemiNaiveEvaluationTest() {\n    super(new InterpretedSemiNaiveTester(false));\n  }\n}\n"
  },
  {
    "path": "src/test/java/edu/harvard/seas/pl/formulog/eval/Tester.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.eval;\n\nimport java.util.List;\n\npublic interface Tester {\n\n  void test(String file, List<String> inputDirs);\n}\n"
  },
  {
    "path": "src/test/java/edu/harvard/seas/pl/formulog/magic/CommonMagicSetTest.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.magic;\n\nimport edu.harvard.seas.pl.formulog.eval.AbstractEvaluationTest;\nimport edu.harvard.seas.pl.formulog.eval.Evaluation;\nimport edu.harvard.seas.pl.formulog.eval.Tester;\nimport org.junit.Test;\n\npublic abstract class CommonMagicSetTest<T extends Evaluation> extends AbstractEvaluationTest<T> {\n\n  public CommonMagicSetTest(Tester tester) {\n    super(tester);\n  }\n\n  @Test\n  public void test140() {\n    test(\"test140_ok.flg\");\n  }\n\n  @Test\n  public void test141() {\n    test(\"test141_ok.flg\");\n  }\n\n  @Test\n  public void test142() {\n    test(\"test142_ok.flg\");\n  }\n\n  @Test\n  public void test143() {\n    test(\"test143_ok.flg\");\n  }\n\n  @Test\n  public void test144() {\n    test(\"test144_ok.flg\");\n  }\n\n  @Test\n  public void test145() {\n    test(\"test145_ok.flg\");\n  }\n\n  @Test\n  public void test146() {\n    test(\"test146_ok.flg\");\n  }\n\n  @Test\n  public void test147() {\n    test(\"test147_ok.flg\");\n  }\n\n  @Test\n  public void test148() {\n    test(\"test148_ok.flg\");\n  }\n\n  @Test\n  public void test149() {\n    test(\"test149_ok.flg\");\n  }\n\n  @Test\n  public void test150() {\n    test(\"test150_ok.flg\");\n  }\n\n  @Test\n  public void test151() {\n    test(\"test151_ok.flg\");\n  }\n\n  @Test\n  public void test152() {\n    test(\"test152_ok.flg\");\n  }\n\n  @Test\n  public void test153() {\n    test(\"test153_ok.flg\");\n  }\n\n  @Test\n  public void test154() {\n    test(\"test154_ok.flg\");\n  }\n\n  @Test\n  public void test155() {\n    test(\"test155_ok.flg\");\n  }\n\n  @Test\n  public void test156() {\n    test(\"test156_ok.flg\");\n  }\n\n  @Test\n  public void test157() {\n    test(\"test157_ok.flg\");\n  }\n\n  @Test\n  public void test158() {\n    test(\"test158_ok.flg\");\n  }\n\n  @Test\n  public void test159() {\n    test(\"test159_ok.flg\");\n  }\n\n  @Test\n  public void test160() {\n    test(\"test160_ok.flg\");\n  }\n\n  @Test\n  public void test161() {\n    test(\"test161_ok.flg\");\n  }\n\n  @Test\n  public void test162() {\n    test(\"test162_ok.flg\");\n  }\n\n  @Test\n  public void test163() {\n    test(\"test163_ok.flg\");\n  }\n\n  @Test\n  public void test164() {\n    test(\"test164_ok.flg\");\n  }\n\n  @Test\n  public void test165() {\n    test(\"test165_ok.flg\");\n  }\n\n  @Test\n  public void test166() {\n    test(\"test166_ok.flg\");\n  }\n\n  @Test\n  public void test167() {\n    test(\"test167_ok.flg\");\n  }\n\n  @Test\n  public void test168() {\n    test(\"test168_ok.flg\");\n  }\n\n  @Test\n  public void test169() {\n    test(\"test169_ok.flg\");\n  }\n\n  @Test\n  public void test170() {\n    test(\"test170_ok.flg\");\n  }\n\n  @Test\n  public void test171() {\n    test(\"test171_ok.flg\");\n  }\n\n  @Test\n  public void test172() {\n    test(\"test172_ok.flg\");\n  }\n\n  @Test\n  public void test173() {\n    test(\"test173_ok.flg\");\n  }\n\n  @Test\n  public void test174() {\n    test(\"test174_ok.flg\");\n  }\n\n  @Test\n  public void test175() {\n    test(\"test175_ok.flg\");\n  }\n\n  @Test\n  public void test176() {\n    test(\"test176_ok.flg\");\n  }\n\n  @Test\n  public void test177() {\n    test(\"test177_ok.flg\");\n  }\n\n  @Test\n  public void test178() {\n    test(\"test178_ok.flg\");\n  }\n\n  @Test\n  public void test179() {\n    test(\"test179_ok.flg\");\n  }\n\n  @Test\n  public void test249() {\n    test(\"test249_ok.flg\");\n  }\n\n  @Test\n  public void test250() {\n    test(\"test250_ok.flg\");\n  }\n\n  @Test\n  public void test252() {\n    test(\"test252_ok.flg\");\n  }\n\n  @Test\n  public void test253() {\n    test(\"test253_ok.flg\");\n  }\n}\n"
  },
  {
    "path": "src/test/java/edu/harvard/seas/pl/formulog/magic/EagerSemiNaiveMagicSetTest.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.magic;\n\nimport edu.harvard.seas.pl.formulog.eval.InterpretedSemiNaiveTester;\nimport edu.harvard.seas.pl.formulog.eval.SemiNaiveEvaluation;\n\npublic class EagerSemiNaiveMagicSetTest extends CommonMagicSetTest<SemiNaiveEvaluation> {\n\n  public EagerSemiNaiveMagicSetTest() {\n    super(new InterpretedSemiNaiveTester(true));\n  }\n}\n"
  },
  {
    "path": "src/test/java/edu/harvard/seas/pl/formulog/magic/SemiNaiveMagicSetTest.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.magic;\n\nimport edu.harvard.seas.pl.formulog.eval.InterpretedSemiNaiveTester;\nimport edu.harvard.seas.pl.formulog.eval.SemiNaiveEvaluation;\n\npublic class SemiNaiveMagicSetTest extends CommonMagicSetTest<SemiNaiveEvaluation> {\n\n  public SemiNaiveMagicSetTest() {\n    super(new InterpretedSemiNaiveTester(false));\n  }\n}\n"
  },
  {
    "path": "src/test/java/edu/harvard/seas/pl/formulog/parsing/ParsingTest.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2024 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.parsing;\n\nimport static org.junit.Assert.fail;\n\nimport java.io.FileNotFoundException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport org.junit.Test;\n\npublic class ParsingTest {\n\n  void test(String file) {\n    boolean isBad = file.matches(\"test\\\\d\\\\d\\\\d_bd.flg\");\n    try {\n      InputStream is = getClass().getClassLoader().getResourceAsStream(file);\n      if (is == null) {\n        throw new FileNotFoundException(file + \" not found\");\n      }\n      (new Parser()).parse(new InputStreamReader(is));\n    } catch (ParseException e) {\n      if (!isBad) {\n        fail(\"Test failed for a good program: \" + e.getMessage());\n      }\n      return;\n    } catch (Exception e) {\n      fail(\"Unexpected exception: \" + e.getMessage() + \" \" + e.getClass());\n    }\n    if (isBad) {\n      fail(\"Test succeeded for a bad program\");\n    }\n  }\n\n  @Test\n  public void test001() {\n    test(\"test001_ok.flg\");\n  }\n\n  @Test\n  public void test002() {\n    test(\"test002_ok.flg\");\n  }\n\n  @Test\n  public void test003() {\n    test(\"test003_ok.flg\");\n  }\n\n  @Test\n  public void test004() {\n    test(\"test004_ok.flg\");\n  }\n\n  @Test\n  public void test005() {\n    test(\"test005_ok.flg\");\n  }\n\n  @Test\n  public void test006() {\n    test(\"test006_ok.flg\");\n  }\n\n  @Test\n  public void test007() {\n    test(\"test007_ok.flg\");\n  }\n\n  @Test\n  public void test028() {\n    test(\"test028_ok.flg\");\n  }\n\n  @Test\n  public void test031() {\n    test(\"test031_ok.flg\");\n  }\n\n  @Test\n  public void test036() {\n    test(\"test036_ok.flg\");\n  }\n\n  @Test\n  public void test042() {\n    test(\"test042_ok.flg\");\n  }\n\n  @Test\n  public void test055() {\n    test(\"test055_bd.flg\");\n  }\n\n  @Test\n  public void test059() {\n    test(\"test059_ok.flg\");\n  }\n\n  @Test\n  public void test074() {\n    test(\"test074_ok.flg\");\n  }\n\n  @Test\n  public void test075() {\n    test(\"test075_ok.flg\");\n  }\n\n  @Test\n  public void test076() {\n    test(\"test076_ok.flg\");\n  }\n\n  @Test\n  public void test255() {\n    test(\"test255_bd.flg\");\n  }\n\n  @Test\n  public void test257() {\n    test(\"test257_ok.flg\");\n  }\n\n  @Test\n  public void test266() {\n    test(\"test266_bd.flg\");\n  }\n\n  @Test\n  public void test271() {\n    test(\"test271_bd.flg\");\n  }\n\n  @Test\n  public void test272() {\n    test(\"test272_bd.flg\");\n  }\n\n  @Test\n  public void test302() {\n    test(\"test302_bd.flg\");\n  }\n\n  @Test\n  public void test322() {\n    test(\"test322_bd.flg\");\n  }\n\n  @Test\n  public void test335() {\n    test(\"test335_bd.flg\");\n  }\n\n  @Test\n  public void test341() {\n    test(\"test341_bd.flg\");\n  }\n\n  @Test\n  public void test342() {\n    test(\"test342_bd.flg\");\n  }\n\n  @Test\n  public void test343() {\n    test(\"test343_bd.flg\");\n  }\n\n  @Test\n  public void test344() {\n    test(\"test344_bd.flg\");\n  }\n}\n"
  },
  {
    "path": "src/test/java/edu/harvard/seas/pl/formulog/types/TypeCheckingTest.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.types;\n\nimport static org.junit.Assert.fail;\n\nimport edu.harvard.seas.pl.formulog.ast.BasicProgram;\nimport edu.harvard.seas.pl.formulog.parsing.Parser;\nimport java.io.FileNotFoundException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport org.junit.Test;\n\npublic class TypeCheckingTest {\n\n  void test(String file) {\n    boolean isBad = file.matches(\"test\\\\d\\\\d\\\\d_bd.flg\");\n    try {\n      InputStream is = getClass().getClassLoader().getResourceAsStream(file);\n      if (is == null) {\n        throw new FileNotFoundException(file + \" not found\");\n      }\n      BasicProgram prog = (new Parser()).parse(new InputStreamReader(is));\n      (new TypeChecker(prog)).typeCheck();\n    } catch (TypeException e) {\n      if (!isBad) {\n        fail(\"Test failed for a good program: \" + e.getMessage());\n      }\n      return;\n    } catch (Exception e) {\n      fail(\"Unexpected exception: \" + e.getMessage());\n    }\n    if (isBad) {\n      fail(\"Test succeeded for a bad program\");\n    }\n  }\n\n  @Test\n  public void test006() {\n    test(\"test006_ok.flg\");\n  }\n\n  @Test\n  public void test008() {\n    test(\"test008_ok.flg\");\n  }\n\n  @Test\n  public void test009() {\n    test(\"test009_ok.flg\");\n  }\n\n  @Test\n  public void test010() {\n    test(\"test010_ok.flg\");\n  }\n\n  @Test\n  public void test011() {\n    test(\"test011_ok.flg\");\n  }\n\n  @Test\n  public void test012() {\n    test(\"test012_bd.flg\");\n  }\n\n  @Test\n  public void test013() {\n    test(\"test013_ok.flg\");\n  }\n\n  @Test\n  public void test014() {\n    test(\"test014_ok.flg\");\n  }\n\n  @Test\n  public void test015() {\n    test(\"test015_ok.flg\");\n  }\n\n  @Test\n  public void test016() {\n    test(\"test016_ok.flg\");\n  }\n\n  @Test\n  public void test017() {\n    test(\"test017_ok.flg\");\n  }\n\n  @Test\n  public void test049() {\n    test(\"test049_ok.flg\");\n  }\n\n  @Test\n  public void test052() {\n    test(\"test052_bd.flg\");\n  }\n\n  @Test\n  public void test053() {\n    test(\"test053_ok.flg\");\n  }\n\n  @Test\n  public void test054() {\n    test(\"test054_ok.flg\");\n  }\n\n  @Test\n  public void test060() {\n    test(\"test060_ok.flg\");\n  }\n\n  @Test\n  public void test080() {\n    test(\"test080_bd.flg\");\n  }\n\n  @Test\n  public void test091() {\n    test(\"test091_bd.flg\");\n  }\n\n  @Test\n  public void test098() {\n    test(\"test098_bd.flg\");\n  }\n\n  @Test\n  public void test118() {\n    test(\"test118_bd.flg\");\n  }\n\n  @Test\n  public void test130() {\n    test(\"test130_bd.flg\");\n  }\n\n  @Test\n  public void test131() {\n    test(\"test131_bd.flg\");\n  }\n\n  @Test\n  public void test132() {\n    test(\"test132_bd.flg\");\n  }\n\n  @Test\n  public void test185() {\n    test(\"test185_bd.flg\");\n  }\n\n  @Test\n  public void test188() {\n    test(\"test188_bd.flg\");\n  }\n\n  @Test\n  public void test268() {\n    test(\"test268_bd.flg\");\n  }\n\n  @Test\n  public void test269() {\n    test(\"test269_bd.flg\");\n  }\n\n  @Test\n  public void test270() {\n    test(\"test270_bd.flg\");\n  }\n\n  @Test\n  public void test293() {\n    test(\"test293_ok.flg\");\n  }\n\n  @Test\n  public void test294() {\n    test(\"test294_ok.flg\");\n  }\n\n  @Test\n  public void test295() {\n    test(\"test295_bd.flg\");\n  }\n\n  @Test\n  public void test296() {\n    test(\"test296_bd.flg\");\n  }\n\n  @Test\n  public void test307() {\n    test(\"test307_ok.flg\");\n  }\n\n  @Test\n  public void test312() {\n    test(\"test312_bd.flg\");\n  }\n\n  @Test\n  public void test313() {\n    test(\"test313_bd.flg\");\n  }\n\n  @Test\n  public void test314() {\n    test(\"test314_bd.flg\");\n  }\n\n  @Test\n  public void test315() {\n    test(\"test315_bd.flg\");\n  }\n\n  @Test\n  public void test327() {\n    test(\"test327_bd.flg\");\n  }\n\n  @Test\n  public void test333() {\n    test(\"test333_bd.flg\");\n  }\n}\n"
  },
  {
    "path": "src/test/java/edu/harvard/seas/pl/formulog/validating/SemiNaiveValidatingTest.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.validating;\n\nimport edu.harvard.seas.pl.formulog.eval.Evaluation;\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport edu.harvard.seas.pl.formulog.eval.SemiNaiveEvaluation;\nimport edu.harvard.seas.pl.formulog.types.WellTypedProgram;\n\npublic class SemiNaiveValidatingTest extends ValidatingTest {\n\n  @Override\n  protected Evaluation setup(WellTypedProgram prog)\n      throws InvalidProgramException, EvaluationException {\n    return SemiNaiveEvaluation.setup(prog, 2, false);\n  }\n}\n"
  },
  {
    "path": "src/test/java/edu/harvard/seas/pl/formulog/validating/ValidatingTest.java",
    "content": "/*-\n * #%L\n * Formulog\n * %%\n * Copyright (C) 2019-2023 President and Fellows of Harvard College\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 * #L%\n */\npackage edu.harvard.seas.pl.formulog.validating;\n\nimport static org.junit.Assert.fail;\n\nimport edu.harvard.seas.pl.formulog.ast.BasicProgram;\nimport edu.harvard.seas.pl.formulog.eval.Evaluation;\nimport edu.harvard.seas.pl.formulog.eval.EvaluationException;\nimport edu.harvard.seas.pl.formulog.parsing.Parser;\nimport edu.harvard.seas.pl.formulog.types.TypeChecker;\nimport edu.harvard.seas.pl.formulog.types.WellTypedProgram;\nimport java.io.FileNotFoundException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport org.junit.Test;\n\npublic abstract class ValidatingTest {\n\n  void test(String file) {\n    boolean isBad = file.matches(\"test\\\\d\\\\d\\\\d_bd.flg\");\n    try {\n      InputStream is = getClass().getClassLoader().getResourceAsStream(file);\n      if (is == null) {\n        throw new FileNotFoundException(file + \" not found\");\n      }\n      BasicProgram prog = (new Parser()).parse(new InputStreamReader(is));\n      setup(new TypeChecker(prog).typeCheck());\n      if (isBad) {\n        fail(\"Test succeeded for a bad program\");\n      }\n    } catch (InvalidProgramException e) {\n      if (!isBad) {\n        fail(\"Test failed for a good program\");\n      }\n    } catch (Exception e) {\n      fail(\"Unexpected exception: \" + e.getMessage());\n    }\n  }\n\n  protected abstract Evaluation setup(WellTypedProgram prog)\n      throws InvalidProgramException, EvaluationException;\n\n  @Test\n  public void test025() {\n    test(\"test025_bd.flg\");\n  }\n\n  @Test\n  public void test026() {\n    test(\"test026_bd.flg\");\n  }\n\n  @Test\n  public void test050() {\n    test(\"test050_bd.flg\");\n  }\n\n  @Test\n  public void test051() {\n    test(\"test051_bd.flg\");\n  }\n\n  @Test\n  public void test133() {\n    test(\"test133_ok.flg\");\n  }\n\n  @Test\n  public void test217() {\n    test(\"test217_bd.flg\");\n  }\n\n  @Test\n  public void test218() {\n    test(\"test218_bd.flg\");\n  }\n\n  @Test\n  public void test219() {\n    test(\"test219_ok.flg\");\n  }\n\n  @Test\n  public void test248() {\n    test(\"test248_bd.flg\");\n  }\n\n  @Test\n  public void test251() {\n    test(\"test251_bd.flg\");\n  }\n}\n"
  },
  {
    "path": "src/test/resources/test001_ok.flg",
    "content": "(* okay *)\n@edb rel p0.\n@edb rel p1(i32).\n@edb rel p2(i32, i32).\n@edb rel p3(i32, i32, i32).\n@edb rel p4(i32, i32, i32, i32)."
  },
  {
    "path": "src/test/resources/test002_ok.flg",
    "content": "(* okay *)\nrel p0.\nrel p1(i32).\nrel p2(i32, i32).\nrel p3(i32, i32, i32).\nrel p4(i32, i32, i32, i32)."
  },
  {
    "path": "src/test/resources/test003_ok.flg",
    "content": "(* okay *)\nfun f0 : i32 = 42.\nfun f1(_X0:i32) : i32 = 42.\nfun f2(_X0:i32, _X1:i32) : i32 = 42.\nfun f3(_X0:i32, _X1:i32, _X2:i32) : i32 = 42.\nfun f4(_X0:i32, _X1:i32, _X2:i32, _X3:i32) : i32 = 42.\n"
  },
  {
    "path": "src/test/resources/test004_ok.flg",
    "content": "type i32_alias = i32.\ntype i32x2 = (i32 * i32).\ntype i32x3 = (i32 * i32 * i32).\ntype i32x4 = (i32 * i32 * i32 * i32).\ntype i32_alias_pair = (i32_alias * i32_alias).\n\ntype i32_list = i32_list_nil | i32_list_cons(i32, i32_list).\n\ntype 'a my_list =\n  | my_nil\n  | my_cons('a, 'a my_list).\n\ntype string_alias = string.\ntype i32_list_2 = i32 my_list.\ntype ('k, 'v) assoc_list = ('k * 'v) my_list.\ntype 'a alias = 'a.\n"
  },
  {
    "path": "src/test/resources/test005_ok.flg",
    "content": "type 'a my_list =\n  | my_nil\n  | my_cons('a, 'a my_list).\n\ntype ('k, 'v) assoc_list = ('k * 'v) my_list.\ntype 'a my_option =\n  | my_none\n  | my_some('a).\n\nfun lookup(Key:'k, Map:('k, 'v) assoc_list) : 'v my_option =\n\tmatch Map with\n\t| my_nil => my_none\n\t(* this doesn't actually do what you think, since key is rebound in the\n\t   pattern *)\n\t| my_cons((_Key, Val), _) => my_some(Val)\n\t| my_cons(_, Rest) => lookup(Key, Rest)\n\tend.\n"
  },
  {
    "path": "src/test/resources/test006_ok.flg",
    "content": "type node = i32.\n@edb rel edge(node, node).\n@edb rel vertex(node).\nrel notWellFormed.\nrel reach(node, node).\nrel inCycle(node).\nrel notInCycle(node).\nrel hasOtherNeighbor(node).\n\nnotWellFormed :- edge(X, _), !vertex(X).\nnotWellFormed :- edge(_, X), !vertex(X).\n\nreach(X, Y) :- edge(X, Y).\nreach(X, Y) :- edge(X, Z), reach(Z, Y).\n\ninCycle(X) :- reach(X, Y), X = Y.\n\nnotInCycle(X) :- vertex(X), !inCycle(X).\n\nhasOtherNeighbor(X) :- edge(X, Y), X != Y."
  },
  {
    "path": "src/test/resources/test007_ok.flg",
    "content": "@edb rel p(string).\n\np(\"hello\").\np(\"\")."
  },
  {
    "path": "src/test/resources/test008_ok.flg",
    "content": "@edb rel p0.\n@edb rel p_i32(i32).\n@edb rel p_i32_2x(i32, i32).\n@edb rel p_string(string).\n@edb rel p_string_2x(string, string).\n\np0.\n\np_i32(0).\np_i32(-42).\np_i32(42).\n\np_i32_2x(0, 0).\np_i32_2x(0, -42).\np_i32_2x(0, 42).\np_i32_2x(-42, 0).\np_i32_2x(-42, -42).\np_i32_2x(-42, 42).\np_i32_2x(42, 0).\np_i32_2x(42, -42).\np_i32_2x(42, 42).\n\np_string(\"\").\np_string(\"hello\").\np_string(\"goodbye\").\n\np_string_2x(\"\", \"\").\np_string_2x(\"\", \"hello\").\np_string_2x(\"\", \"goodbye\").\np_string_2x(\"hello\", \"\").\np_string_2x(\"hello\", \"hello\").\np_string_2x(\"hello\", \"goodbye\").\np_string_2x(\"goodbye\", \"\").\np_string_2x(\"goodbye\", \"hello\").\np_string_2x(\"goodbye\", \"goodbye\")."
  },
  {
    "path": "src/test/resources/test009_ok.flg",
    "content": "@edb rel p0.\ntype 'a alias = 'a.\n@edb rel p_i32(i32 alias).\n@edb rel p_i32_2x(i32 alias, i32 alias).\n@edb rel p_string(string alias).\n@edb rel p_string_2x(string alias, string alias).\n\np0.\n\np_i32(0).\np_i32(-42).\np_i32(42).\n\np_i32_2x(0, 0).\np_i32_2x(0, -42).\np_i32_2x(0, 42).\np_i32_2x(-42, 0).\np_i32_2x(-42, -42).\np_i32_2x(-42, 42).\np_i32_2x(42, 0).\np_i32_2x(42, -42).\np_i32_2x(42, 42).\n\np_string(\"\").\np_string(\"hello\").\np_string(\"goodbye\").\n\np_string_2x(\"\", \"\").\np_string_2x(\"\", \"hello\").\np_string_2x(\"\", \"goodbye\").\np_string_2x(\"hello\", \"\").\np_string_2x(\"hello\", \"hello\").\np_string_2x(\"hello\", \"goodbye\").\np_string_2x(\"goodbye\", \"\").\np_string_2x(\"goodbye\", \"hello\").\np_string_2x(\"goodbye\", \"goodbye\")."
  },
  {
    "path": "src/test/resources/test010_ok.flg",
    "content": "@edb rel p0.\ntype 'a alias = 'a.\ntype i32_alias = i32 alias.\ntype string_alias = string alias.\n@edb rel p_i32(i32_alias).\n@edb rel p_i32_2x(i32_alias, i32_alias).\n@edb rel p_string(string_alias).\n@edb rel p_string_2x(string_alias, string_alias).\n\np0.\n\np_i32(0).\np_i32(-42).\np_i32(42).\n\np_i32_2x(0, 0).\np_i32_2x(0, -42).\np_i32_2x(0, 42).\np_i32_2x(-42, 0).\np_i32_2x(-42, -42).\np_i32_2x(-42, 42).\np_i32_2x(42, 0).\np_i32_2x(42, -42).\np_i32_2x(42, 42).\n\np_string(\"\").\np_string(\"hello\").\np_string(\"goodbye\").\n\np_string_2x(\"\", \"\").\np_string_2x(\"\", \"hello\").\np_string_2x(\"\", \"goodbye\").\np_string_2x(\"hello\", \"\").\np_string_2x(\"hello\", \"hello\").\np_string_2x(\"hello\", \"goodbye\").\np_string_2x(\"goodbye\", \"\").\np_string_2x(\"goodbye\", \"hello\").\np_string_2x(\"goodbye\", \"goodbye\")."
  },
  {
    "path": "src/test/resources/test011_ok.flg",
    "content": "type ('a, 'b) pair = ('a * 'b).\n@edb rel p_i32_string((i32, string) pair).\ntype i32_pair = (i32, i32) pair. \n@edb rel pair_of_i32_pair((i32_pair, i32_pair) pair).\n\np_i32_string((0, \"\")).\np_i32_string((42, \"hello\")).\np_i32_string((-42, \"goodbye\")).\n\npair_of_i32_pair(((0, 0), (-42, 42))).\npair_of_i32_pair(((-42, 0), (0, 42)))."
  },
  {
    "path": "src/test/resources/test012_bd.flg",
    "content": "@edb rel str(string).\n@edb rel num(i32).\nrel foo(i32, string).\n\nfoo(X, Y) :- str(X), str(Y)."
  },
  {
    "path": "src/test/resources/test013_ok.flg",
    "content": "@edb rel rel1(i32, string).\n@edb rel rel2(i32, string).\nrel join(i32, (string * string)).\n\njoin(Id, (Str1, Str2)) :- rel1(Id, Str1), rel2(Id, Str2).\n\n(* an equivalent (more verbose) formulation: *)\njoin(Id1, Pair) :- rel1(Id1, Str1), rel2(Id2, Str2), Id1 = Id2, Pair = (Str1, Str2).\n"
  },
  {
    "path": "src/test/resources/test014_ok.flg",
    "content": "fun f(X:i32) : i32 = X.\nfun g(X:'a) : 'a = X.\nfun h(_X:string) : i32 = 42.\nfun h_inv(_X:i32) : string = \"hello\".\n\n@edb rel q(i32).\nrel p(i32).\n\np(Y) :- Y = f(X), q(X).\np(Y) :- Y = g(X), q(X).\np(Y) :- Y = f(h(g(h_inv(X)))), q(X).\n"
  },
  {
    "path": "src/test/resources/test015_ok.flg",
    "content": "type ('a, 'b) union =\n  | inj_l('a)\n  | inj_r('b).\n@edb rel q((i32, string) union).\nrel p(i32).\nrel r(string).\n\np(X) :- q(inj_l(X)).\nr(X) :- q(inj_r(X)).\n"
  },
  {
    "path": "src/test/resources/test016_ok.flg",
    "content": "type ('a, 'b) union =\n  | inj_l('a)\n  | inj_r('b).\ntype color = (i32, (i32*i32*i32)) union.\ntype id = (string, i32) union.\n\nfun str2i32(_S:string) : i32 = 42.\n\n@edb rel colors(color).\n@edb rel ids(id).\n\nrel gray(i32, i32).\nrel rgb(i32, i32, i32, i32).\n\ngray(Id, Color) :- ids(inj_r(Id)), colors(inj_l(Color)).\ngray(Id, Color) :- ids(inj_l(Id_str)), colors(inj_l(Color)), Id = str2i32(Id_str).\nrgb(Id, R, G, B) :- ids(inj_r(Id)), colors(inj_r((R, G, B))).\nrgb(Id, R, G, B) :- ids(inj_l(Id_str)), colors(inj_r((R, G, B))), Id = str2i32(Id_str).\n"
  },
  {
    "path": "src/test/resources/test017_ok.flg",
    "content": "type nat =\n  | o\n  | s(nat).\n\nfun lookup(Idx:nat, List:'a list) : 'a option =\n  match (Idx, List) with\n  | (_, []) => none\n  | (o, X :: _) => some(X)\n  | (s(Idx), _ :: List) => lookup(Idx, List)\n  end.\n\nfun loop : 'a = loop.\ntype false_term = .\nfun false_exists : false_term = loop.\n\nfun id(X:'a) : 'a = X.\n"
  },
  {
    "path": "src/test/resources/test018_ok.flg",
    "content": "@edb rel r.\nrel q.\nrel p.\nrel ok.\n\np :- q.\nq :- r.\nr.\n\nok :- p, q, r."
  },
  {
    "path": "src/test/resources/test019_ok.flg",
    "content": "@edb rel r(i32).\nrel q(i32).\nrel p(i32).\nrel ok.\n\np(X) :- q(X).\nq(X) :- r(X).\nr(-42).\nr(0).\nr(42).\n\nok :-\n\tr(-42), r(0), r(42),\n\tq(-42), q(0), q(42),\n\tp(-42), p(0), p(42)."
  },
  {
    "path": "src/test/resources/test020_ok.flg",
    "content": "type node = i32.\n@edb rel edge(node, node).\n@edb rel vertex(node).\nrel notWellFormed.\nrel reach(node, node).\nrel inCycle(node).\nrel notInCycle(node).\nrel hasOtherNeighbor(node).\nrel ok.\n\nnotWellFormed :- edge(X, _), !vertex(X).\nnotWellFormed :- edge(_, X), !vertex(X).\n\nreach(X, Y) :- edge(X, Y).\nreach(X, Y) :- edge(X, Z), reach(Z, Y).\n\ninCycle(X) :- reach(X, Y), X = Y.\n\nnotInCycle(X) :- vertex(X), !inCycle(X).\n\nhasOtherNeighbor(X) :- edge(X, Y), X != Y.\n\nvertex(0).\nvertex(1).\nvertex(2).\nvertex(3).\nvertex(4).\nedge(0, 1).\nedge(1, 2).\nedge(2, 3).\nedge(3, 4).\nedge(4, 4).\n\nok :- \n\tvertex(0), vertex(1), vertex(2), vertex(3), vertex(4),\n\tedge(0, 1), edge(1, 2), edge(2, 3), edge(3, 4), edge(4, 4),\n\t!notWellFormed, \n\treach(0, 1), reach(0, 2), reach(0, 3), reach(0, 4),\n\treach(1, 2), reach(1, 3), reach(1, 4),\n\treach(2, 3), reach(2, 4), reach(3, 4),\n\treach(4, 4),\n\tinCycle(4),\n\tnotInCycle(0), notInCycle(1), notInCycle(2), notInCycle(3),\n\thasOtherNeighbor(0), hasOtherNeighbor(1),\n\thasOtherNeighbor(2), hasOtherNeighbor(3),\n\t!hasOtherNeighbor(4)."
  },
  {
    "path": "src/test/resources/test021_ok.flg",
    "content": "rel ok.\n\n@edb rel input_list(i32 list).\nrel palindrome(i32 list).\nrel fail.\n\nfun list_rev_helper(Xs:'a list, Acc:'a list) : 'a list =\n\tmatch Xs with\n\t| [] => Acc\n\t| X :: Rest => list_rev_helper(Rest, X :: Acc)\n\tend.\nfun list_rev(Xs:'a list) : 'a list = list_rev_helper(Xs, []).\n\npalindrome(Xs) :- input_list(Xs), list_rev(Xs) = Xs.\n\nfail :- input_list(Xs), palindrome(Xs), list_rev(Xs) != Xs.\n\ninput_list([]).\ninput_list([1]).\ninput_list([1, 42, 1]).\ninput_list([1, 42, 42, 1]).\ninput_list([1, 2, 3, 4]).\n\nok :-\n\tpalindrome([]),\n\tpalindrome([1]),\n\tpalindrome([1, 42, 1]),\n\tpalindrome([1, 42, 42, 1]),\n\t!palindrome([1, 2, 3, 4]),\n\t!fail.\n"
  },
  {
    "path": "src/test/resources/test022_ok.flg",
    "content": "rel base_ok.\nrel neg_ok.\nrel and_ok.\nrel or_ok.\nrel ok.\n\nbase_ok :-\n\tis_sat(`true`),\n\t!is_sat(`false`).\n\nneg_ok :-\n\t!is_sat(`~true`),\n\tis_sat(`~false`),\n\tis_sat(`~~true`),\n\t!is_sat(`~~false`).\n\nand_ok :-\n\tis_sat(`true /\\ true`),\n\t!is_sat(`true /\\ false`),\n\t!is_sat(`false /\\ true`),\n\t!is_sat(`false /\\ false`).\n\t\nor_ok :-\n\tis_sat(`true \\/ true`),\n\tis_sat(`true \\/ false`),\n\tis_sat(`false \\/ true`),\n\t!is_sat(`false \\/ false`).\n\t\nok :-\n\tbase_ok,\n\tneg_ok,\n\tand_ok,\n\tor_ok."
  },
  {
    "path": "src/test/resources/test023_ok.flg",
    "content": "rel ok.\n\nok :-\n\tis_sat(`42 #= 42`),\n\tX = `42`,\n\tY = X,\n\tis_sat(`X #= Y`),\n\tZ = `43`,\n\t!is_sat(`Y #= Z`),\n\tis_sat(`~(Y #= Z)`).\n"
  },
  {
    "path": "src/test/resources/test024_ok.flg",
    "content": "rel ok.\n\nok :-\n\tX = 42,\n\t0 = X + 2 + -44,\n\tX = 20 + 22,\n\tX = 2 + 20 * 2,\n\tX = 20 * 2 + 2,\n\tY = 20 * (2 + 2),\n\tY = 80,\n\tY / (2 + 2) = 20 % 21,\n\tX = 41 - -1.\n"
  },
  {
    "path": "src/test/resources/test025_bd.flg",
    "content": "@edb rel p.\nrel fail(i32).\n\nfail(_X) :- p.\n"
  },
  {
    "path": "src/test/resources/test026_bd.flg",
    "content": "rel ok.\n\nfun f(X:'a) : 'a = X.\n\ntype blah =\n  | a(i32, i32, i32, i32, i32)\n  | b(i32, i32, i32).\n\nok :- a(f(X3), f(X2), f(X1), f(X0), 0) = a(_X4, X3, X2, X1, X0).\n"
  },
  {
    "path": "src/test/resources/test027_ok.flg",
    "content": "type node = i32.\n@edb rel edge(node, node).\n@edb rel vertex(node).\nrel notWellFormed.\nrel reach(node, node).\nrel inCycle(node).\nrel notInCycle(node).\nrel hasOtherNeighbor(node).\nrel ok.\n\nnotWellFormed :- edge(X, _), !vertex(X).\nnotWellFormed :- edge(_, X), !vertex(X).\n\nreach(X, Y) :- edge(X, Y).\nreach(X, Y) :- reach(X, Z), reach(Z, Y).\n\ninCycle(X) :- reach(X, Y), X = Y.\n\nnotInCycle(X) :- vertex(X), !inCycle(X).\n\nhasOtherNeighbor(X) :- edge(X, Y), X != Y.\n\nvertex(0).\nvertex(1).\nvertex(2).\nvertex(3).\nvertex(4).\nedge(0, 1).\nedge(1, 2).\nedge(2, 3).\nedge(3, 4).\nedge(4, 4).\n\nok :- \n\tvertex(0), vertex(1), vertex(2), vertex(3), vertex(4),\n\tedge(0, 1), edge(1, 2), edge(2, 3), edge(3, 4), edge(4, 4),\n\t!notWellFormed, \n\treach(0, 1), reach(0, 2), reach(0, 3), reach(0, 4),\n\treach(1, 2), reach(1, 3), reach(1, 4),\n\treach(2, 3), reach(2, 4), reach(3, 4),\n\treach(4, 4),\n\tinCycle(4),\n\tnotInCycle(0), notInCycle(1), notInCycle(2), notInCycle(3),\n\thasOtherNeighbor(0), hasOtherNeighbor(1),\n\thasOtherNeighbor(2), hasOtherNeighbor(3),\n\t!hasOtherNeighbor(4).\n"
  },
  {
    "path": "src/test/resources/test028_ok.flg",
    "content": "@edb rel foo(i64).\n\nfoo(42l).\nfoo(42L).\nfoo(-42l).\nfoo(-42L).\nfoo(+42l).\nfoo(+42L)."
  },
  {
    "path": "src/test/resources/test029_ok.flg",
    "content": "@edb rel foo(i64).\n\nfoo(0L).\nfoo(-1L).\n\nrel add(i64, i64, i64).\n\nadd(X, Y, Z) :-\n\tfoo(X),\n\tfoo(Y),\n\tZ = i64_add(X, Y).\n\nrel ok.\nok :-\n\tadd(0L, 0L, 0L),\n\tadd(0L, -1L, -1L),\n\tadd(-0L, -1L, -1L),\n\tadd(-1L, -1L, -2L)."
  },
  {
    "path": "src/test/resources/test030_ok.flg",
    "content": "rel something_true.\nrel something_false1.\nrel something_false2.\nrel ok.\n\nsomething_true :-\n\tX = #{\"x\"}[bv[64]],\n\tY = `bv_big_const[64](42L)`,\n\tF = `X #= Y`,\n\tis_sat(F),\n\tis_sat(`~F`).\n\nsomething_false1 :-\n\tX = #x[bv[64]],\n\tY = `bv_big_const[64](42L)`,\n\tF = `X #= Y`,\n\tis_sat(`F /\\ ~F`).\n\nsomething_false2 :-\n\tX = #{\"x\"}[bv[64]],\n\tY = #x[bv[64]],\n  is_sat(`~(X #= Y)`).\n\nok :- something_true, !something_false1, !something_false2.\n"
  },
  {
    "path": "src/test/resources/test031_ok.flg",
    "content": "@edb rel foo(fp32).\n@edb rel bar(fp64).\n\nfoo(42.0f).\nfoo(42.0e1f).\nfoo(42.0e-1f).\nfoo(42.0e+1f).\nfoo(-42.42F).\nfoo(-0.42F).\nfoo(-42F).\nfoo(-42.0e1f).\nfoo(-42.0e-1f).\nfoo(-42.0e+1f).\nfoo(+42.42F).\nfoo(+0.42F).\nfoo(+42F).\nfoo(+42.0e1f).\nfoo(+42.0e-1f).\nfoo(+42.0e+1f).\n\nbar(42.0d).\nbar(42.0e1d).\nbar(42.0e-1d).\nbar(42.0e+1d).\nbar(-42.42D).\nbar(-0.42D).\nbar(-42D).\nbar(-42.0e1d).\nbar(-42.0e-1d).\nbar(-42.0e+1d).\nbar(+42.42D).\nbar(+0.42D).\nbar(+42D).\nbar(+42.0e1d).\nbar(+42.0e-1d).\nbar(+42.0e+1d)."
  },
  {
    "path": "src/test/resources/test032_ok.flg",
    "content": "@edb rel foo(fp32).\n\nfoo(0F).\nfoo(-1F).\n\nrel add(fp32, fp32, fp32).\n\nadd(X, Y, Z) :-\n\tfoo(X),\n\tfoo(Y),\n\tZ = fp32_add(X, Y).\n\nrel ok.\nok :-\n\tadd(0F, 0F, 0F),\n\tadd(0F, -1F, -1F),\n\tadd(-1F, 0F, -1F),\n\tadd(-1F, -1F, -2F)."
  },
  {
    "path": "src/test/resources/test033_ok.flg",
    "content": "@edb rel foo(fp64).\n\nfoo(0D).\nfoo(-1D).\n\nrel add(fp64, fp64, fp64).\n\nadd(X, Y, Z) :-\n\tfoo(X),\n\tfoo(Y),\n\tZ = fp64_add(X, Y).\n\nrel ok.\nok :-\n\tadd(0D, 0D, 0D),\n\tadd(0D, -1D, -1D),\n\tadd(-1D, 0D, -1D),\n\tadd(-1D, -1D, -2D)."
  },
  {
    "path": "src/test/resources/test034_ok.flg",
    "content": "rel something_true.\nrel something_false.\nrel ok.\n\nsomething_true :-\n\tX = #x[fp[64]],\n\tY = `fp_big_const[64](42D)`,\n\tis_sat(`X #= Y`),\n\tis_sat(`~(X #= Y)`).\n\t\nsomething_false :-\n\tX = #{\"x\"}[fp[64]],\n\tY = `fp_big_const[64](42D)`,\n\tF = `X #= Y`,\n\tis_sat(`F /\\ ~F`).\n\nok :- something_true, !something_false.\n"
  },
  {
    "path": "src/test/resources/test035_ok.flg",
    "content": "rel something_true.\nrel something_false.\nrel ok.\n\nsomething_true :-\n\tX = #x[fp[32]],\n\tY = `fp_const[32](42F)`,\n\tF = `X #= Y`,\n\tis_sat(F),\n\tis_sat(`~F`).\n\t\nsomething_false :-\n\tX = #{\"x\"}[fp[32]],\n\tY = `fp_const[32](42F)`,\n\tF = `X #= Y`,\n\tis_sat(`F /\\ ~F`).\n\nok :- something_true, !something_false.\n"
  },
  {
    "path": "src/test/resources/test036_ok.flg",
    "content": "@edb rel foo(fp32).\n@edb rel bar(fp64).\n\nfoo(fp32_nan).\nfoo(fp32_neg_infinity).\nfoo(fp32_pos_infinity).\n\nbar(fp64_nan).\nbar(fp64_neg_infinity).\nbar(fp64_pos_infinity)."
  },
  {
    "path": "src/test/resources/test037_ok.flg",
    "content": "rel something_true.\nrel something_false1.\nrel something_false2.\nrel ok.\n\nsomething_true :-\n\tfp32_nan = fp32_nan,\n\tfp64_nan = fp64_nan.\n\t\nsomething_false1 :-\n\tfp32_eq(fp32_nan, fp32_nan) = true.\n\t\nsomething_false2 :-\n\tfp64_eq(fp64_nan, fp64_nan) = true.\n\nok :- something_true, !something_false1, !something_false2."
  },
  {
    "path": "src/test/resources/test038_ok.flg",
    "content": "rel ok.\n\nok :-\n\n\tis_sat(`0 #= bv_to_bv_unsigned[64,32](0L)`),\n\tis_sat(`0 #= fp_to_sbv[32,32](0F)`),\n\tis_sat(`0 #= fp_to_sbv[64,32](0D)`),\n\tis_sat(`0 #= fp_to_ubv[32,32](0F)`),\n\tis_sat(`0 #= fp_to_ubv[64,32](0D)`),\n\n\tis_sat(`0L #= bv_to_bv_unsigned[32,64](0)`),\n\tis_sat(`0L #= fp_to_sbv[32,64](0F)`),\n\tis_sat(`0L #= fp_to_sbv[64,64](0D)`),\n\tis_sat(`0L #= fp_to_ubv[32,64](0F)`),\n\tis_sat(`0L #= fp_to_ubv[64,64](0D)`),\n\n\tis_sat(`0F #= bv_to_fp[32,32](0)`),\n\tis_sat(`0F #= bv_to_fp[64,32](0L)`),\n\tis_sat(`0F #= fp_to_fp[64,32](0D)`),\n\n\tis_sat(`0D #= bv_to_fp[32,64](0)`),\n\tis_sat(`0D #= bv_to_fp[64,64](0L)`),\n\tis_sat(`0D #= fp_to_fp[32,64](0F)`),\n\t\n\ttrue = true.\n"
  },
  {
    "path": "src/test/resources/test039_ok.flg",
    "content": "rel ok.\n\nok :-\n\n  X = #x[fp[32]],\n\t!is_sat(`fp_lt(fp32_pos_infinity, X)`),\n\t!is_sat(`fp_gt(fp32_neg_infinity, X)`),\n\t!is_sat(`fp_eq(fp32_nan, fp32_nan)`),\n\tis_valid(`fp_is_nan(fp32_nan)`),\n\t\n  Y = #x[fp[64]],\n\t!is_sat(`fp_lt(fp64_pos_infinity, Y)`),\n\t!is_sat(`fp_gt(fp64_neg_infinity, Y)`),\n\t!is_sat(`fp_eq(fp64_nan, fp64_nan)`),\n\tis_valid(`fp_is_nan(fp64_nan)`).\n"
  },
  {
    "path": "src/test/resources/test040_ok.flg",
    "content": "rel ok.\n\nok :-\n\n\tis_sat(`#if true then true else false`),\n\t!is_sat(`#if true then false else true`),\n\n\tis_sat(`(#if true then 1 else 0) #= 1`),\n\t!is_sat(`(#if false then 1 else 0) #= 1`),\n\n\tis_sat(`(#if true then 1L else 0L) #= 1L`),\n\t!is_sat(`(#if false then 1L else 0L) #= 1L`),\n\n\tis_sat(`(#if true then 1F else 0F) #= 1F`),\n\t!is_sat(`(#if false then 1F else 0F) #= 1F`),\n\n\tis_sat(`(#if true then 1D else 0D) #= 1D`),\n\t!is_sat(`(#if false then 1D else 0D) #= 1D`),\n\n  true = true.\n"
  },
  {
    "path": "src/test/resources/test041_ok.flg",
    "content": "(* test how fp conversion works. *)\n\nrel ok.\n\nok :-\n\t\n\tis_valid(`fp_is_nan(fp_to_fp[64,32](fp64_nan))`),\n\tis_valid(`fp32_pos_infinity #= fp_to_fp[64,32](fp64_pos_infinity)`),\n\tis_valid(`fp32_neg_infinity #= fp_to_fp[64,32](fp64_neg_infinity)`),\n\t\n\tis_valid(`fp_is_nan(fp_to_fp[32,64](fp32_nan))`),\n\tis_valid(`fp64_pos_infinity #= fp_to_fp[32,64](fp32_pos_infinity)`),\n\tis_valid(`fp64_neg_infinity #= fp_to_fp[32,64](fp32_neg_infinity)`).\n"
  },
  {
    "path": "src/test/resources/test042_ok.flg",
    "content": "type prim =\n  | prim_byte(bv[32] smt)\n  | prim_short(bv[32] smt).\n\ntype java_prim_type =\n  | java_prim_type_int.\n\t\nfun handle_concrete_conversion(V:prim, Type:java_prim_type) : prim =\n  match Type with\n  | java_prim_type_int =>\n    let Vv = \n      match V with\n      | prim_byte(_X) => `42`\n      | prim_byte(X)\n      | prim_short(X) => X\n      end in\n    prim_short(Vv)\n  end.\t\n"
  },
  {
    "path": "src/test/resources/test043_ok.flg",
    "content": "@edb rel foo(i32).\nrel ok.\n\nfoo(42).\n\nfun bar(X:i32) : bool = foo(X).\n\nok :- bar(42) = true, bar(0) = false.\n"
  },
  {
    "path": "src/test/resources/test044_ok.flg",
    "content": "@edb rel foo(i32, i32).\nrel ok.\n\nfun bar(X:i32) : bool = foo(X, 13).\n\nfun baz(X:i32) : bool = foo(17, X).\n\nfoo(42, 13).\nfoo(17, 42).\n\nok :-\n\tbar(42) = true,\n\tbar(0) = false,\n\tbaz(42) = true,\n\tbaz(0) = false.\n\t\t\n"
  },
  {
    "path": "src/test/resources/test045_ok.flg",
    "content": "@edb rel foo(i32, i32)\nrel ok\n\nfoo(42, 13).\nfoo(17, 42).\n\nok :-\n\tX = 42,\n\tY = 17,\n\tZ = 13,\n\tfoo(X, Y) = false,\n\tfoo(X, Z) = true,\n\tfoo(Y, X) = true,\n\tfoo(Y, Z) = false,\n\tfoo(Z, X) = false,\n\tfoo(Z, Y) = false.\n"
  },
  {
    "path": "src/test/resources/test046_ok.flg",
    "content": "@edb rel foo(i32, bool).\n@edb rel bar(i32).\nrel ok.\n\nfoo(42, true).\nfoo(0, false).\nbar(42).\n\nok :-\n\tfoo(42, bar(42)),\n\tfoo(0, bar(0))."
  },
  {
    "path": "src/test/resources/test047_ok.flg",
    "content": "@edb rel foo(i32, bool).\n@edb rel bar(i32).\nrel ok.\n\nfoo(42, true).\nfoo(0, false).\nbar(42).\n\nok :-\n\tfoo(X, bar(X)),\n\tfoo(Y, bar(Y)),\n\tX != Y."
  },
  {
    "path": "src/test/resources/test048_ok.flg",
    "content": "@edb rel foo(i32, bool).\nrel ok.\n\nfoo(42, true).\nfoo(0, false).\n\nfun bar(X:i32) : bool = X = 42.\n\nok :-\n\tfoo(X, bar(X)),\n\tfoo(Y, bar(Y)),\n\tX != Y.\n"
  },
  {
    "path": "src/test/resources/test049_ok.flg",
    "content": "fun f(Xs:'a list) : 'a list = Xs.\n\nfun g(Xs:string list, Ys:i32 list) : (string list * i32 list) =\n  (f(Xs), f(Ys))."
  },
  {
    "path": "src/test/resources/test050_bd.flg",
    "content": "rel foo(i32).\n\nfun bar(X:i32) : i32 = if foo(X) then -X else X.\n\nfoo(X) :- bar(42) = X.\n"
  },
  {
    "path": "src/test/resources/test051_bd.flg",
    "content": "rel foo(i32).\n\nfun bar(X:i32) : i32 = if baz(X) then -X else X\nand baz(X:i32) : bool = foo(X).\n\nfoo(X) :- bar(42) = X.\n"
  },
  {
    "path": "src/test/resources/test052_bd.flg",
    "content": "@edb rel foo(bv[32] smt).\n\nfoo(`42L`).\n"
  },
  {
    "path": "src/test/resources/test053_ok.flg",
    "content": "@edb rel foo(bv[32] smt).\n\nfoo(`42`).\nfoo(`bv_add(42, 77)`).\nfoo(`bv_to_bv_unsigned[64,32](128L)`).\n"
  },
  {
    "path": "src/test/resources/test054_ok.flg",
    "content": "uninterpreted fun foo(bv[32] smt, fp[32] smt) : bool smt.\n"
  },
  {
    "path": "src/test/resources/test055_bd.flg",
    "content": "type bool_expr = foo(i32, fp_expr[24,8]).\n"
  },
  {
    "path": "src/test/resources/test056_ok.flg",
    "content": "rel ok.\nrel something_true.\nrel something_false.\n\nuninterpreted fun foo(bv[32] smt) : bv[32] smt.\n\nfun exp1 : bool smt = `foo(42) #= 0`.\nfun exp2 : bool smt = `foo(42) #= 1`.\n\nsomething_true :- is_sat(exp1), is_sat(exp2).\n\nsomething_false :- is_sat(`exp1 /\\ exp2`).\n\nok :- something_true, !something_false.\n"
  },
  {
    "path": "src/test/resources/test057_ok.flg",
    "content": "rel ok.\nrel something_true.\nrel something_false.\n\nuninterpreted fun foo(bv[32] smt) : fp[32] smt.\n\nfun exp1 : bool smt = `foo(42) #= 0F`.\nfun exp2 : bool smt = `foo(42) #= 1F`.\n\nsomething_true :- is_sat(exp1), is_sat(exp2).\n\nsomething_false :- is_sat(`exp1 /\\ exp2`).\n\nok :- something_true, !something_false.\n"
  },
  {
    "path": "src/test/resources/test058_ok.flg",
    "content": "rel ok.\nrel something_true.\nrel something_false.\n\nuninterpreted fun foo(bv[32] smt) : bool smt.\n\nfun exp1 : bool smt = `foo(42) #= true`.\nfun exp2 : bool smt = `foo(42) #= false`.\n\nsomething_true :- is_sat(exp1), is_sat(exp2).\n\nsomething_false :- is_sat(`exp1 /\\ exp2`).\n\nok :- something_true, !something_false.\n"
  },
  {
    "path": "src/test/resources/test059_ok.flg",
    "content": "type foo =\n  | bar\n  | baz1(i32)\n  | baz2(string)\n  | baz3(i32)\n  | baz4(string).\n"
  },
  {
    "path": "src/test/resources/test060_ok.flg",
    "content": "fun bar : 'a list = [].\n\nrel foo.\nfoo :-\n  _X = 1 :: 2 :: bar.\n"
  },
  {
    "path": "src/test/resources/test061_ok.flg",
    "content": "rel ok\nok.\n\n(*\nrel ok1.\nok1 :-\n  X = #x[bv[32]],\n  Y = #y[bv[32]],\n  Z = #z[bv[32]],\n  E1 = `42 #= bv_add(X, Y)`,\n  E2 = `42 #= bv_add(Z, Y)`,\n  substitute(X, `Z`, E1) = E2. \n \t\nrel ok2.\nok2 :-\n  X = \"x\",\n  Y = \"y\",\n  Z = \"z\",\n  E1 = `42 #= bv_add(#{X}[bv[32]], #{Y}[bv[32]])`,\n  E2 = `42 #= bv_add(#{Z}[bv[32]], #{Y}[bv[32]])`,\n  substitute(#{X}[bv[32]], `#{Z}[bv[32]]`, E1) = E2.\n\nrel ok3.\nok3 :-\n  X = #x[bv[32]],\n  Y = #y[bv[32]],\n  E =\n    `#let X = Y in\n     #let X = 42 in\n     bv_add(X, X) #= 84`,\n  is_valid(E).\n\nrel ok4.\nok4 :-\n  X = #x[bv[32]],\n  E =\n    `#let X = 42 in\n     bv_add(X, X) #= 84`,\n  is_valid(substitute(X, `0`, E)).\n\n(* Make sure that we are avoiding variable capture correctly. *)\nrel ok5.\nok5 :-\n  X = #x[bv[32]],\n  Y = #y[bv[32]],\n  E =\n    `#let X = 0 in\n     bv_add(Y, Y) #= 42`,\n  is_sat(substitute(Y, `X`, E)).\n\n(* Make sure that substitute is deterministic. *)\nrel ok6.\nok6 :-\n  X = #x[bv[32]],\n  Y = #y[bv[32]],\n  E = `#let X = 42 in bv_add(X, Y)`,\n  substitute(Y, `21`, E) = substitute(Y, `21`, E).\n \t\nrel ok.\nok :- ok1, ok2, ok3, ok4, ok5, ok6.\n*)"
  },
  {
    "path": "src/test/resources/test062_ok.flg",
    "content": "@edb rel bar(i32).\nbar(2).\n\nrel foo(i32, i32, i32).\nfoo(1, 2, 1 + 2).\nfoo(X, Y, X + Y) :- bar(X), bar(Y).\n\nrel ok.\nok :- foo(1,2,3), foo(2,2,4)."
  },
  {
    "path": "src/test/resources/test063_ok.flg",
    "content": "rel ok.\nok :-\n  A = #a[bool],\n  B = #b[bool],\n  is_valid(`A /\\ B ==> A \\/ B`),\n  !is_valid(`A \\/ B ==> A /\\ B`).\n"
  },
  {
    "path": "src/test/resources/test064_ok.flg",
    "content": "type foo =\n  | foo1\n  | foo2.\n\nrel ok.\nok :-\n  _ = `foo1 #= foo2`.\n"
  },
  {
    "path": "src/test/resources/test065_ok.flg",
    "content": "type foo =\n  | foo1\n  | foo2.\n\nrel ok.\nok :-\n\tX = `foo1 #= foo2`,\n\t!is_sat(X).\n"
  },
  {
    "path": "src/test/resources/test066_ok.flg",
    "content": "type foo =\n  | foo1\n  | foo2.\n\nrel ok.\nok :-\n\tX = `#x[foo] #= foo2`,\n\tis_sat(X).\n"
  },
  {
    "path": "src/test/resources/test067_ok.flg",
    "content": "type foo =\n  | foo1\n  | foo2.\n\nrel ok.\nok :-\n\tX = #x[foo],\n\tY = `X #= foo1`,\n\tis_sat(Y),\n\tZ = `X #= foo2`,\n\tis_sat(Z).\n"
  },
  {
    "path": "src/test/resources/test068_ok.flg",
    "content": "type foo =\n  | foo1\n  | foo2.\n\nrel ok.\nok :-\n\tX = #x[foo],\n\tY = `X #= foo1`,\n\tis_sat(Y),\n\tZ = `X #= foo2`,\n\tis_sat(Z),\n\t!is_sat(`Y /\\ Z`).\n"
  },
  {
    "path": "src/test/resources/test069_ok.flg",
    "content": "type 'a my_list =\n\t| my_nil\n\t| my_cons('a, 'a my_list)\n\nrel ok\nok :-\n\tX = #x[i32 my_list],\n\tis_sat(`X #= my_nil`).\n"
  },
  {
    "path": "src/test/resources/test070_ok.flg",
    "content": "type 'a my_list =\n\t| my_nil\n\t| my_cons('a, 'a my_list).\n\nrel ok.\nok :-\n\tX = #x[i32 my_list],\n\tis_sat(`X #= my_cons(42, my_nil)`).\n"
  },
  {
    "path": "src/test/resources/test071_ok.flg",
    "content": "type 'a my_list =\n\t| my_nil\n\t| my_cons('a, 'a my_list).\n\nrel ok.\nok :-\n\tX = #x[i32 my_list],\n\tY = `X #= my_cons(42, my_nil)`,\n\tis_sat(Y),\n\tZ = `X #= my_nil`,\n\tis_sat(Z),\n\t!is_sat(`Y /\\ Z`).\n"
  },
  {
    "path": "src/test/resources/test072_ok.flg",
    "content": "rel ok.\nok :-\n\tX = #x[i32 list],\n\tY = `X #= [1, 2, 3]`,\n\tis_sat(Y),\n\tZ = `X #= []`,\n\tis_sat(Z),\n\t!is_sat(`Y /\\ Z`).\n"
  },
  {
    "path": "src/test/resources/test073_ok.flg",
    "content": "rel ok.\nok :-\n\tX = #x[i32 option],\n\tY = `X #= some(42)`,\n\tis_sat(Y),\n\tZ = `X #= none`,\n\tis_sat(Z),\n\t!is_sat(`Y /\\ Z`).\n"
  },
  {
    "path": "src/test/resources/test074_ok.flg",
    "content": "type foo =\n  | foo1\nand bar =\n  | bar1.\n"
  },
  {
    "path": "src/test/resources/test075_ok.flg",
    "content": "type 'a foo =\n  | foo1(i32)\n  | foo2('a)\nand ('a, 'b) bar =\n  | bar1('a)\n  | bar2('b). "
  },
  {
    "path": "src/test/resources/test076_ok.flg",
    "content": "type 'a foo =\n  | foo1(('a, 'a) bar)\n  | foo2('a foo)\nand ('a, 'b) bar =\n  | bar1('a foo)\n  | bar2('b foo)\n  | bar3(('a foo, 'b foo) bar). "
  },
  {
    "path": "src/test/resources/test077_ok.flg",
    "content": "type foo =\n  | foo1(i32)\n  | foo2(bar)\nand bar =\n  | bar1(i32)\n  | bar2(foo).\n  \nrel ok.\nok :-\n  X = #x[foo],\n  Y = #y[bar],\n  Eq1 = `X #= foo1(42)`,\n  Eq2 = `Y #= bar2(X)`,\n  is_sat(`Eq1 /\\ Eq2`).\n"
  },
  {
    "path": "src/test/resources/test078_ok.flg",
    "content": "type 'a foo =\n  | foo1(i32)\n  | foo2('a bar)\nand 'a bar =\n  | bar1('a)\n  | bar2('a foo).\n  \nrel ok.\nok :-\n  X = #x[i32 foo],\n  Y = #y[i32 bar],\n  Eq1 = `X #= foo1(42)`,\n  Eq2 = `Y #= bar2(X)`,\n  is_sat(`Eq1 /\\ Eq2`).\n"
  },
  {
    "path": "src/test/resources/test079_ok.flg",
    "content": "rel ok.\nok :-\n  X = #x[i32],\n  Eq1 = `X #= 42`,\n  Eq2 = `X #= 99`,\n  is_sat(Eq1),\n  is_sat(Eq2),\n  !is_sat(`Eq1 /\\ Eq2`).\n"
  },
  {
    "path": "src/test/resources/test080_bd.flg",
    "content": "rel bad.\n\nbad :- _ = `#let 42 = 42 in 42`.\n"
  },
  {
    "path": "src/test/resources/test081_ok.flg",
    "content": "rel ok.\n\nok :-\n  X = #x[bool],\n  is_sat(`#let X = true in X`),\n  !is_sat(`#let X = false in X`),\n  is_sat(`#let X = false in #let X = true in X`),\n  !is_sat(`#let X = true in #let X = false in X`).\n"
  },
  {
    "path": "src/test/resources/test082_ok.flg",
    "content": "type 'a my_list =\n  | my_nil\n  | my_cons('a, 'a my_list).\n\nrel ok.\nok :-\n  X = #x[i32 my_list],\n  E = `#is_my_nil(X)`,\n  F = `#is_my_cons(X)`,\n  is_sat(E),\n  is_sat(F),\n  !is_sat(`E /\\ F`).\n"
  },
  {
    "path": "src/test/resources/test083_ok.flg",
    "content": "rel ok.\nok :-\n  X = #x[i32 list],\n  E = `#is_nil(X)`,\n  F = `#is_cons(X)`,\n  is_sat(E),\n  is_sat(F),\n  !is_sat(`E /\\ F`).\n"
  },
  {
    "path": "src/test/resources/test084_ok.flg",
    "content": "type 'a my_option =\n  | my_none\n  | my_some('a).\n\nrel ok.\nok :-\n  X = #x[i32 my_option],\n  E = `#my_some_1(X) #= 0`,\n  F = `#my_some_1(X) #= 42`,\n  is_sat(E),\n  is_sat(F),\n  !is_sat(`E /\\ F`).\n"
  },
  {
    "path": "src/test/resources/test085_ok.flg",
    "content": "rel ok.\nok :-\n  X = #x[i32 option],\n  E = `#some_1(X) #= 0`,\n  F = `#some_1(X) #= 42`,\n  is_sat(E),\n  is_sat(F),\n  !is_sat(`E /\\ F`).\n"
  },
  {
    "path": "src/test/resources/test086_ok.flg",
    "content": "rel ok.\nok :-\n  X1 = #x[i32],\n  L1 = `[1, X1, 2]`,\n  X2 = #x[string],\n  L2 = `[\"foo\", X2, \"baz\"]`,\n  E = `#cons_1(#cons_2(L1)) #= 42`,\n  F = `#cons_1(#cons_2(L2)) #= \"bar\"`,\n  is_sat(E),\n  is_sat(F),\n  is_sat(`E /\\ F`).\n"
  },
  {
    "path": "src/test/resources/test087_ok.flg",
    "content": "rel ok.\nok :-\n  X = #x[i32],\n  L = `[1, X, 2]`,\n  Y = `#cons_1(#cons_2(L))`,\n  E = `Y #= 42`,\n  F = `Y #= -42`,\n  G = `Y #= X`,\n  is_sat(E),\n  is_sat(F),\n  !is_sat(`E /\\ F`),\n  is_valid(G).\n\n"
  },
  {
    "path": "src/test/resources/test088_ok.flg",
    "content": "rel ok.\nok :- is_valid(`smt_eq[bool list]([], [])`)."
  },
  {
    "path": "src/test/resources/test089_ok.flg",
    "content": "rel ok1.\nok1 :-\n  X = #{1}[bool],\n  Y = #{\"2\"}[bool],\n  X != Y,\n  is_sat(`X #= Y`),\n  !is_valid(`X #= Y`).\n\nrel ok2.\nok2 :-\n  X = #{1}[bool],\n  Y = #y[bool],\n  X != Y,\n  is_sat(`X #= Y`),\n  !is_valid(`X #= Y`).\n\nrel ok3.\nok3 :-\n  is_sat(`#{1}[bool] #= #{\"2\"}[bool]`),\n  !is_valid(`#{1}[bool] #= #{\"2\"}[bool]`).\n\nrel ok4.\nok4 :-\n  X = #{1}[bool],\n  Y = #{1}[bool],\n  X = Y,\n  is_valid(`X #= Y`).\n\nrel ok5.\nok5 :-\n  is_valid(`#{1}[bool] #= #{1}[bool]`).\n\nrel ok.\nok :- ok1, ok2, ok3, ok4, ok5.\n"
  },
  {
    "path": "src/test/resources/test090_ok.flg",
    "content": "type foo = a | b | c.\n\nrel common(foo, i32).\ncommon(a, 0).\ncommon(b, 1).\ncommon(c, 2).\n\nrel special(foo, i32).\nspecial(a, 5).\n\nrel op(foo, i32).\nop(X, Res) :-\n  common(X, Res),\n  X not a.\nop(a, Res) :-\n  special(a, Res).\n\nrel ok.\nok :- \n  op(a, 5), !op(a, 0),\n  !(a not a), b not a, c not a,\n  a not b, !(b not b), c not b,\n  a not c, b not c, !(c not c).\n"
  },
  {
    "path": "src/test/resources/test091_bd.flg",
    "content": "type foo = | a. \n\nrel ok.\nok :- !a not a. \n"
  },
  {
    "path": "src/test/resources/test092_ok.flg",
    "content": "@edb rel foo(i32).\n\nfun incr(X: i32) : i32 = X + 1.\nfun zero : i32 = 0.\n\nfoo(incr(41)).\nfoo(zero).\nfoo(-12 - 30).\n\nrel ok.\nok :- foo(42), foo(0), foo(-42)."
  },
  {
    "path": "src/test/resources/test093_ok.flg",
    "content": "rel ok1.\nok1 :- true.\n\nrel ok2.\nok2 :- !false.\n\nrel ok3.\nok3 :- X = true, X.\n\nrel ok.\nok :- ok1, ok2, ok3."
  },
  {
    "path": "src/test/resources/test094_ok.flg",
    "content": "rel ok.\nok :- !is_sat(`forall #x[bool] . #x[bool] #= #y[bool]`)."
  },
  {
    "path": "src/test/resources/test095_ok.flg",
    "content": "rel ok.\nok :- !is_sat(`exists #x[bool] . ~(#x[bool] #= #x[bool])`)."
  },
  {
    "path": "src/test/resources/test096_ok.flg",
    "content": "rel ok\nok.\n\n(*\nrel ok.\nok :-\n\tF = `forall #x[bool] . #x[bool] #= #y[bool]`,\n\tG = substitute(#y[bool], `#x[bool]`, F),\n\t!is_sat(G).\n*)"
  },
  {
    "path": "src/test/resources/test097_ok.flg",
    "content": "rel ok\nok.\n\n(*\nrel ok.\nok :-\n\tF = `exists #x[bool] . ~(#x[bool] #= #y[bool])`,\n\tG = substitute(#y[bool], `#x[bool]`, F),\n\tis_valid(G).\n*)"
  },
  {
    "path": "src/test/resources/test098_bd.flg",
    "content": "rel foo(i32).\n\n:- foo(\"hello\")."
  },
  {
    "path": "src/test/resources/test099_ok.flg",
    "content": "rel ok.\nok :- \n  none = get_model([`#x[bool]`, `~#x[bool]`], none),\n  some(M) = get_model([`#x[bool]`, `~#y[bool]`], none),\n  query_model(#x[bool], M) = some(true),\n  query_model(#y[bool], M) = some(false),\n  query_model(#z[bool], M) = none.\n"
  },
  {
    "path": "src/test/resources/test100_ok.flg",
    "content": "rel ok.\nok :-\n  some(M) = get_model([`(#x[i32] #= 42)`, `(#y[i32] #= bv_neg(#x[i32]))`], none),\n  query_model(#x[i32], M) = some(42),\n  query_model(#y[i32], M) = some(-42).\n"
  },
  {
    "path": "src/test/resources/test101_ok.flg",
    "content": "rel ok.\nok :-\n  some(M) = get_model([`#x[i64] #= 42L`, `#y[i64] #= bv_neg(#x[i64])`]),\n  query_model(#x[i64], M) = some(42L),\n  query_model(#y[i64], M) = some(-42L).\n"
  },
  {
    "path": "src/test/resources/test102_ok.flg",
    "content": "rel ok.\nok :-\n  some(M) = get_model([`#x[fp32] #= 42F`, `#y[fp32] #= fp_neg(#x[fp32])`], none),\n  query_model(#x[fp32], M) = some(42F),\n  query_model(#y[fp32], M) = some(-42F).\n"
  },
  {
    "path": "src/test/resources/test103_ok.flg",
    "content": "rel ok1.\nok1 :-\n  some(M) = get_model([`fp_is_nan(#x[fp32])`], none),\n  query_model(#x[fp32], M) = some(fp32_nan).\n  \nrel ok2.\nok2 :-\n  some(M) = get_model([`#x[fp32] #= fp32_pos_infinity`], none),\n  query_model(#x[fp32], M) = some(fp32_pos_infinity).\n  \nrel ok3.\nok3 :-\n  some(M) = get_model([`#x[fp32] #= fp32_neg_infinity`], none),\n  query_model(#x[fp32], M) = some(fp32_neg_infinity).\n  \nrel ok.\nok :- ok1, ok2, ok3.\n"
  },
  {
    "path": "src/test/resources/test104_ok.flg",
    "content": "rel ok.\nok :-\n  some(M) = get_model([`(#x[fp64] #= 42D)`, `#y[fp64] #= fp_neg(#x[fp64])`], none),\n  query_model(#x[fp64], M) = some(42D),\n  query_model(#y[fp64], M) = some(-42D).\n"
  },
  {
    "path": "src/test/resources/test105_ok.flg",
    "content": "rel ok1.\nok1 :-\n  some(M) = get_model([`fp_is_nan(#x[fp64])`], none),\n  query_model(#x[fp64], M) = some(fp64_nan).\n  \nrel ok2.\nok2 :-\n  some(M) = get_model([`#x[fp64] #= fp64_pos_infinity`], none),\n  query_model(#x[fp64], M) = some(fp64_pos_infinity).\n  \nrel ok3.\nok3 :-\n  some(M) = get_model([`#x[fp64] #= fp64_neg_infinity`], none),\n  query_model(#x[fp64], M) = some(fp64_neg_infinity).\n  \nrel ok.\nok :- ok1, ok2, ok3.\n"
  },
  {
    "path": "src/test/resources/test106_ok.flg",
    "content": "fun msg : string = \"Hello, \\\"world\\\"!\\nGoodbye!\".\n\nrel ok.\nok :-\n  some(M) = get_model([`#x[string] #= msg`], none),\n  query_model(#x[string], M) = some(msg).\n"
  },
  {
    "path": "src/test/resources/test107_ok.flg",
    "content": "fun msg : string = \"Hello, \\\"world\\\"!\\nGoodbye!\".\n\nrel ok.\nok :-\n  some(M) = get_model([`#x[bool]`, `#y[string] #= msg`, `~#z[bool]`], none),\n  query_model(#x[bool], M) = some(true),\n  query_model(#y[string], M) = some(msg),\n  query_model(#z[bool], M) = some(false).\n"
  },
  {
    "path": "src/test/resources/test108_ok.flg",
    "content": "rel ok.\nok :-\n  some(M) = get_model([`#is_nil(#x[bool list])`, `~#is_nil(#y[bool list])`], none),\n  query_model(#x[bool list], M) = some([]),\n  query_model(#y[bool list], M) = some(_ :: _).\n"
  },
  {
    "path": "src/test/resources/test109_ok.flg",
    "content": "rel ok.\nok :-\n  some(M) = get_model([`#is_nil(#x[i32 list])`, `~#is_nil(#y[i32 list])`], none),\n  query_model(#x[i32 list], M) = some([]),\n  query_model(#y[i32 list], M) = some(_ :: _).\n"
  },
  {
    "path": "src/test/resources/test110_ok.flg",
    "content": "rel ok.\nok :-\n  some(M) = get_model([`#x[bv[16]] #= bv_const[16](42)`, `#y[i32] #= bv_to_bv_signed[16,32](#x[bv[16]])`], none),\n  query_model(#x[bv[16]], M) = none,\n  query_model(#y[bv[32]], M) = some(42).\n"
  },
  {
    "path": "src/test/resources/test111_ok.flg",
    "content": "rel ok.\nok :-\n  some(M) = get_model([`#x[bv[16]] #= bv_big_const[16](42L)`, `#y[i32] #= bv_to_bv_signed[16,32](#x[bv[16]])`], none),\n  query_model(#x[bv[16]], M) = none,\n  query_model(#y[bv[32]], M) = some(42).\n"
  },
  {
    "path": "src/test/resources/test112_ok.flg",
    "content": "rel ok.\nok :-\n  some(M) = get_model([`#is_nil(#x[bv[16] list])`], none),\n  query_model(#x[bv[16] list], M) = none.\n"
  },
  {
    "path": "src/test/resources/test113_ok.flg",
    "content": "rel ok.\nok :-\n  some(M) = get_model([`#x[bv[16]] #= bv_const[16](-1)`, `#y[i32] #= bv_to_bv_signed[16,32](#x[bv[16]])`], none),\n  query_model(#x[bv[16]], M) = none,\n  query_model(#y[bv[32]], M) = some(-1).\n"
  },
  {
    "path": "src/test/resources/test114_ok.flg",
    "content": "rel ok.\nok :-\n  some(M) = get_model([`#x[bv[16]] #= bv_big_const[16](-1L)`, `#y[i32] #= bv_to_bv_signed[16,32](#x[bv[16]])`], none),\n  query_model(#x[bv[16]], M) = none,\n  query_model(#y[bv[32]], M) = some(-1).\n"
  },
  {
    "path": "src/test/resources/test115_ok.flg",
    "content": "(* Make sure get_model acts deterministically. *)\n\nrel ok.\nok :-\n  some(M1) = get_model([`~(#x[bv[32]] #= #y[bv[32]])`], none),\n  some(M2) = get_model([`~(#x[bv[32]] #= #y[bv[32]])`], none),\n  M1 = M2.\n"
  },
  {
    "path": "src/test/resources/test116_ok.flg",
    "content": "rel ok.\nok :-\n  some(M) = get_model([`#is_nil(#x[bool list])`, `#is_nil(#y[i32 list])`], none),\n  query_model(#x[bool list], M) = some([]),\n  query_model(#y[i32 list], M) = some([]).\n"
  },
  {
    "path": "src/test/resources/test117_ok.flg",
    "content": "rel ok.\nok :-\n\tis_sat(`exists #x[bv[32]], #y[bv[16]] . bv_to_bv_signed[32,16](#x[bv[32]]) #= #y[bv[16]]`).\n"
  },
  {
    "path": "src/test/resources/test118_bd.flg",
    "content": "rel ok.\nok :-\n\tis_sat(`exists #x[bv[32]], true, #y[bv[16]] . bv_to_bv_signed[32,16](#x[bv[32]]) #= #y[bv[16]]`).\n"
  },
  {
    "path": "src/test/resources/test119_ok.flg",
    "content": "rel ok.\nok :-\n\tis_valid(`forall #x[bool], #x[bool] . #x[bool] #= #x[bool]`)."
  },
  {
    "path": "src/test/resources/test120_ok.flg",
    "content": "uninterpreted fun foo(bool smt) : bool smt.\n\nrel ok.\nok :-\n\tX = #x[bool],\n\tis_valid(`(forall X : foo(X). foo(X) #= X) ==> foo(true)`)."
  },
  {
    "path": "src/test/resources/test121_ok.flg",
    "content": "rel ok.\nok :-\n  A = #a[(i32, i32) array],\n  X = #x[i32],\n  is_valid(`array_select(array_store(A, X, X), X) #= X`)."
  },
  {
    "path": "src/test/resources/test122_ok.flg",
    "content": "type foo = foo1 | foo2(foo, foo).\n\nrel ok.\nok :-\n  A = #a[(foo, foo) array],\n  X = #x[foo],\n  is_valid(`array_select(array_store(A, X, X), X) #= X`)."
  },
  {
    "path": "src/test/resources/test123_ok.flg",
    "content": "rel ok.\nok :-\n  A = #a[(i32, i32) array],\n  X = #x[i32],\n  Y = #y[i32],\n  !is_valid(`array_select(array_store(array_store(A, X, 42), Y, 21), X) #= 42`)."
  },
  {
    "path": "src/test/resources/test124_ok.flg",
    "content": "rel ok.\nok :-\n  A = #a[(i32, i32) array],\n  X = #x[i32],\n  Y = #y[i32],\n  is_valid(`~(X #= Y) ==> array_select(array_store(array_store(A, X, 42), Y, 21), X) #= 42`)."
  },
  {
    "path": "src/test/resources/test125_ok.flg",
    "content": "rel ok.\nok :-\n  I = #x[int],\n  F = `int_add(I, int_big_const(0L)) #= int_mul(I, int_const(1))`,\n  is_valid(F)."
  },
  {
    "path": "src/test/resources/test126_ok.flg",
    "content": "rel ok.\nok :-\n  X = #x[string],\n  Y = #y[string],\n  F = `str_prefixof(X, Y) ==> exists #i[int]. (str_substr(Y, #i[int], str_len(X)) #= X)`,\n  is_valid(F)."
  },
  {
    "path": "src/test/resources/test127_ok.flg",
    "content": "uninterpreted fun foo1(bool smt) : bool smt.\nuninterpreted fun foo2(bool smt) : bool smt.\n\nrel ok.\nok :-\n  X = #x[bool],\n  Y = #y[bool],\n  Axiom = `forall X, Y : foo1(X), foo2(Y). (foo1(X) #= X) \\/ (foo2(Y) #= Y)`,\n  is_valid(`(Axiom /\\ foo1(X) /\\ foo2(X)) ==> X`)."
  },
  {
    "path": "src/test/resources/test128_ok.flg",
    "content": "rel ok\nok.\n\n(*\nrel ok.\nok :-\n\tX = #x[bool],\n\tY = #y[bool],\n\tis_free(X, `X /\\ Y`),\n\tis_free(Y, `X /\\ Y`),\n\t!is_free(Y, `X`),\n\tis_free(X, `#let Y = X in Y`),\n\t!is_free(Y, `#let Y = X in Y`),\n\tis_free(X, `#let X = X in X`),\n\tis_free(X, `forall Y. X`),\n\t!is_free(X, `forall X. X`),\n\t!is_free(X, `forall X, Y. X /\\ Y`),\n\tis_free(Y, `forall X, X. X /\\ Y`).\n*)"
  },
  {
    "path": "src/test/resources/test129_ok.flg",
    "content": "rel ok.\nok :-\n  X = #x[bool],\n  Y = #y[bool],\n  is_sat_opt([`X #= Y`], some(100)) = some(true)."
  },
  {
    "path": "src/test/resources/test130_bd.flg",
    "content": "rel not_ok.\nnot_ok :-\n  X = get_model([`true`], none),\n  _ = `X #= X`.\n"
  },
  {
    "path": "src/test/resources/test131_bd.flg",
    "content": "rel not_ok.\nnot_ok :-\n  X = get_model([`true`], none),\n  _ = `X #= X`.\n"
  },
  {
    "path": "src/test/resources/test132_bd.flg",
    "content": "rel not_ok.\nnot_ok :-\n  X = get_model([`true`], none),\n  Y = [none, X],\n  _ = `#is_nil(Y)`.\n"
  },
  {
    "path": "src/test/resources/test133_ok.flg",
    "content": "@edb rel formula(bool smt).\n\nrel not_ok.\nnot_ok :- formula(X), X = `_Y /\\ _Z`. \n"
  },
  {
    "path": "src/test/resources/test134_ok.flg",
    "content": "uninterpreted sort typ.\n\nuninterpreted fun subtyp(typ smt, typ smt) : bool smt.\n\nrel ok.\nok :-\n\tX = #x[typ],\n\tRefl = `forall X. subtyp(X, X)`,\n\tis_valid(`Refl ==> subtyp(X, X)`)."
  },
  {
    "path": "src/test/resources/test135_ok.flg",
    "content": "uninterpreted sort typ.\n\nuninterpreted fun subtyp(typ smt, typ smt) : bool smt.\n\nrel ok.\nok :-\n\tX = #x[typ],\n\tY = #y[bool],\n\tsome(M) = get_model([`subtyp(X, X)`, `Y`], none),\n\tquery_model(X, M) = none,\n\tquery_model(Y, M) = some(true).\n"
  },
  {
    "path": "src/test/resources/test136_ok.flg",
    "content": "uninterpreted sort ('a, 'b) typ.\n\nuninterpreted fun subtyp((bool, i32) typ smt, (bool, i32) typ smt) : bool smt.\n\nrel ok.\nok :-\n\tX = #x[(bool, i32) typ],\n\tRefl = `forall X. subtyp(X, X)`,\n\tis_valid(`Refl ==> subtyp(X, X)`)."
  },
  {
    "path": "src/test/resources/test137_ok.flg",
    "content": "uninterpreted sort ('a, 'b) typ.\n\nuninterpreted fun subtyp((bool, i32) typ smt, (bool, i32) typ smt) : bool smt.\n\nrel ok.\nok :-\n\tX = #x[(bool, i32) typ],\n\tY = #y[bool],\n\tsome(M) = get_model([`subtyp(X, X)`, `Y`], none),\n\tquery_model(X, M) = none,\n\tquery_model(Y, M) = some(true).\n"
  },
  {
    "path": "src/test/resources/test138_ok.flg",
    "content": "fun a : ('a, i32) array smt = `array_const(0)`.\n\nrel ok.\nok :-\n  X = #x[string],\n  Y = #y[bool],\n  is_valid(`array_select(a, X) #= 0 /\\ array_select(a, Y) #= 0`).\n"
  },
  {
    "path": "src/test/resources/test139_ok.flg",
    "content": "uninterpreted sort sv_map.\n\nuninterpreted fun f((string, i32 option) array smt) : bool smt.\n\nrel ok.\nok :- is_sat(`f(array_const(none))`)."
  },
  {
    "path": "src/test/resources/test140_ok.flg",
    "content": "@edb rel a(i32).\n@edb rel d(i32).\n\n@topdown\nrel b(i32).\n\n@topdown\nrel c(i32, i32).\n\n@topdown\nrel p(i32).\n\n@topdown\nrel q(i32).\n\na(0). a(1). a(2).\nd(0). d(2). d(4).\n\np(X) :- a(X).\n\nb(X) :- d(X).\n\nc(X, Y) :- b(X), p(X), b(Y).\n\nq(X) :- c(X, X).\n\nrel ok.\nok :- q(2).\n"
  },
  {
    "path": "src/test/resources/test141_ok.flg",
    "content": "@edb rel a(i32).\n@edb rel d(i32).\n\n@bottomup\nrel b(i32).\n\n@topdown\nrel c(i32, i32).\n\n@topdown\nrel p(i32).\n\n@topdown\nrel q(i32).\n\na(0). a(1). a(2).\nd(0). d(2). d(4).\n\np(X) :- a(X).\n\nb(X) :- d(X).\n\nc(X, Y) :- b(X), p(X), b(Y).\n\nq(X) :- c(X, X).\n\nrel ok.\nok :- q(2).\n"
  },
  {
    "path": "src/test/resources/test142_ok.flg",
    "content": "@edb rel a(i32).\n@edb rel d(i32).\n\n@topdown\nrel b(i32).\n\n@bottomup\nrel c(i32, i32).\n\n@topdown\nrel p(i32).\n\n@topdown\nrel q(i32).\n\na(0). a(1). a(2).\nd(0). d(2). d(4).\n\np(X) :- a(X).\n\nb(X) :- d(X).\n\nc(X, Y) :- b(X), p(X), b(Y).\n\nq(X) :- c(X, X).\n\nrel ok.\nok :- q(2).\n"
  },
  {
    "path": "src/test/resources/test143_ok.flg",
    "content": "@edb rel a(i32).\n@edb rel d(i32).\n\n@topdown\nrel b(i32).\n\n@topdown\nrel c(i32, i32).\n\n@bottomup\nrel p(i32).\n\n@topdown\nrel q(i32).\n\na(0). a(1). a(2).\nd(0). d(2). d(4).\n\np(X) :- a(X).\n\nb(X) :- d(X).\n\nc(X, Y) :- b(X), p(X), b(Y).\n\nq(X) :- c(X, X).\n\nrel ok.\nok :- q(2).\n"
  },
  {
    "path": "src/test/resources/test144_ok.flg",
    "content": "@edb rel a(i32).\n@edb rel d(i32).\n\n@topdown\nrel b(i32).\n\n@topdown\nrel c(i32, i32).\n\n@topdown\nrel p(i32).\n\n@bottomup\nrel q(i32).\n\na(0). a(1). a(2).\nd(0). d(2). d(4).\n\np(X) :- a(X).\n\nb(X) :- d(X).\n\nc(X, Y) :- b(X), p(X), b(Y).\n\nq(X) :- c(X, X).\n\nrel ok.\nok :- q(2).\n"
  },
  {
    "path": "src/test/resources/test145_ok.flg",
    "content": "@edb rel a(i32).\n@edb rel d(i32).\n\n@bottomup\nrel b(i32).\n\n@bottomup\nrel c(i32, i32).\n\n@topdown\nrel p(i32).\n\n@topdown\nrel q(i32).\n\na(0). a(1). a(2).\nd(0). d(2). d(4).\n\np(X) :- a(X).\n\nb(X) :- d(X).\n\nc(X, Y) :- b(X), p(X), b(Y).\n\nq(X) :- c(X, X).\n\nrel ok.\nok :- q(2).\n"
  },
  {
    "path": "src/test/resources/test146_ok.flg",
    "content": "@edb rel a(i32).\n@edb rel d(i32).\n\n@bottomup\nrel b(i32).\n\n@topdown\nrel c(i32, i32).\n\n@bottomup\nrel p(i32).\n\n@topdown\nrel q(i32).\n\na(0). a(1). a(2).\nd(0). d(2). d(4).\n\np(X) :- a(X).\n\nb(X) :- d(X).\n\nc(X, Y) :- b(X), p(X), b(Y).\n\nq(X) :- c(X, X).\n\nrel ok.\nok :- q(2).\n"
  },
  {
    "path": "src/test/resources/test147_ok.flg",
    "content": "@edb rel a(i32).\n@edb rel d(i32).\n\n@bottomup\nrel b(i32).\n\n@topdown\nrel c(i32, i32).\n\n@topdown\nrel p(i32).\n\n@bottomup\nrel q(i32).\n\na(0). a(1). a(2).\nd(0). d(2). d(4).\n\np(X) :- a(X).\n\nb(X) :- d(X).\n\nc(X, Y) :- b(X), p(X), b(Y).\n\nq(X) :- c(X, X).\n\nrel ok.\nok :- q(2).\n"
  },
  {
    "path": "src/test/resources/test148_ok.flg",
    "content": "@edb rel a(i32).\n@edb rel d(i32).\n\n@topdown\nrel b(i32).\n\n@bottomup\nrel c(i32, i32).\n\n@bottomup\nrel p(i32).\n\n@topdown\nrel q(i32).\n\na(0). a(1). a(2).\nd(0). d(2). d(4).\n\np(X) :- a(X).\n\nb(X) :- d(X).\n\nc(X, Y) :- b(X), p(X), b(Y).\n\nq(X) :- c(X, X).\n\nrel ok.\nok :- q(2).\n"
  },
  {
    "path": "src/test/resources/test149_ok.flg",
    "content": "@edb rel a(i32).\n@edb rel d(i32).\n\n@topdown\nrel b(i32).\n\n@bottomup\nrel c(i32, i32).\n\n@topdown\nrel p(i32).\n\n@bottomup\nrel q(i32).\n\na(0). a(1). a(2).\nd(0). d(2). d(4).\n\np(X) :- a(X).\n\nb(X) :- d(X).\n\nc(X, Y) :- b(X), p(X), b(Y).\n\nq(X) :- c(X, X).\n\nrel ok.\nok :- q(2).\n"
  },
  {
    "path": "src/test/resources/test150_ok.flg",
    "content": "@edb rel a(i32).\n@edb rel d(i32).\n\n@topdown\nrel b(i32).\n\n@topdown\nrel c(i32, i32).\n\n@bottomup\nrel p(i32).\n\n@bottomup\nrel q(i32).\n\na(0). a(1). a(2).\nd(0). d(2). d(4).\n\np(X) :- a(X).\n\nb(X) :- d(X).\n\nc(X, Y) :- b(X), p(X), b(Y).\n\nq(X) :- c(X, X).\n\nrel ok.\nok :- q(2).\n"
  },
  {
    "path": "src/test/resources/test151_ok.flg",
    "content": "@edb rel a(i32).\n@edb rel d(i32).\n\n@bottomup\nrel b(i32).\n\n@bottomup\nrel c(i32, i32).\n\n@bottomup\nrel p(i32).\n\n@topdown\nrel q(i32).\n\na(0). a(1). a(2).\nd(0). d(2). d(4).\n\np(X) :- a(X).\n\nb(X) :- d(X).\n\nc(X, Y) :- b(X), p(X), b(Y).\n\nq(X) :- c(X, X).\n\nrel ok.\nok :- q(2).\n"
  },
  {
    "path": "src/test/resources/test152_ok.flg",
    "content": "@edb rel a(i32).\n@edb rel d(i32).\n\n@bottomup\nrel b(i32).\n\n@bottomup\nrel c(i32, i32).\n\n@topdown\nrel p(i32).\n\n@bottomup\nrel q(i32).\n\na(0). a(1). a(2).\nd(0). d(2). d(4).\n\np(X) :- a(X).\n\nb(X) :- d(X).\n\nc(X, Y) :- b(X), p(X), b(Y).\n\nq(X) :- c(X, X).\n\nrel ok.\nok :- q(2).\n"
  },
  {
    "path": "src/test/resources/test153_ok.flg",
    "content": "@edb rel a(i32).\n@edb rel d(i32).\n\n@bottomup\nrel b(i32).\n\n@topdown\nrel c(i32, i32).\n\n@bottomup\nrel p(i32).\n\n@bottomup\nrel q(i32).\n\na(0). a(1). a(2).\nd(0). d(2). d(4).\n\np(X) :- a(X).\n\nb(X) :- d(X).\n\nc(X, Y) :- b(X), p(X), b(Y).\n\nq(X) :- c(X, X).\n\nrel ok.\nok :- q(2).\n"
  },
  {
    "path": "src/test/resources/test154_ok.flg",
    "content": "@edb rel a(i32).\n@edb rel d(i32).\n\n@topdown\nrel b(i32).\n\n@bottomup\nrel c(i32, i32).\n\n@bottomup\nrel p(i32).\n\n@bottomup\nrel q(i32).\n\na(0). a(1). a(2).\nd(0). d(2). d(4).\n\np(X) :- a(X).\n\nb(X) :- d(X).\n\nc(X, Y) :- b(X), p(X), b(Y).\n\nq(X) :- c(X, X).\n\nrel ok.\nok :- q(2).\n"
  },
  {
    "path": "src/test/resources/test155_ok.flg",
    "content": "@edb rel a(i32).\n@edb rel d(i32).\n\n@bottomup\nrel b(i32).\n\n@bottomup\nrel c(i32, i32).\n\n@bottomup\nrel p(i32).\n\n@bottomup\nrel q(i32).\n\na(0). a(1). a(2).\nd(0). d(2). d(4).\n\np(X) :- a(X).\n\nb(X) :- d(X).\n\nc(X, Y) :- b(X), p(X), b(Y).\n\nq(X) :- c(X, X).\n\nrel ok.\nok :- q(2).\n"
  },
  {
    "path": "src/test/resources/test156_ok.flg",
    "content": "@edb rel a(i32).\n@edb rel d(i32).\n\n@topdown\nrel b(i32).\n\n@topdown\nrel c(i32, i32).\n\n@topdown\nrel p(i32).\n\n@topdown\nrel q(i32).\n\na(0). a(1). a(2).\nd(0). d(2). d(4).\n\np(X) :- a(X).\n\nb(X) :- d(X).\n\nc(X, Y) :- b(X), p(X), b(Y).\n\nq(X) :- c(X, X).\n\nrel ok.\nok :- q(2).\n:- ok.\n"
  },
  {
    "path": "src/test/resources/test157_ok.flg",
    "content": "@edb rel a(i32).\n@edb rel d(i32).\n\n@bottomup\nrel b(i32).\n\n@topdown\nrel c(i32, i32).\n\n@topdown\nrel p(i32).\n\n@topdown\nrel q(i32).\n\na(0). a(1). a(2).\nd(0). d(2). d(4).\n\np(X) :- a(X).\n\nb(X) :- d(X).\n\nc(X, Y) :- b(X), p(X), b(Y).\n\nq(X) :- c(X, X).\n\nrel ok.\nok :- q(2).\n:- ok.\n"
  },
  {
    "path": "src/test/resources/test158_ok.flg",
    "content": "@edb rel a(i32).\n@edb rel d(i32).\n\n@topdown\nrel b(i32).\n\n@bottomup\nrel c(i32, i32).\n\n@topdown\nrel p(i32).\n\n@topdown\nrel q(i32).\n\na(0). a(1). a(2).\nd(0). d(2). d(4).\n\np(X) :- a(X).\n\nb(X) :- d(X).\n\nc(X, Y) :- b(X), p(X), b(Y).\n\nq(X) :- c(X, X).\n\nrel ok.\nok :- q(2).\n:- ok.\n"
  },
  {
    "path": "src/test/resources/test159_ok.flg",
    "content": "@edb rel a(i32).\n@edb rel d(i32).\n\n@topdown\nrel b(i32).\n\n@topdown\nrel c(i32, i32).\n\n@bottomup\nrel p(i32).\n\n@topdown\nrel q(i32).\n\na(0). a(1). a(2).\nd(0). d(2). d(4).\n\np(X) :- a(X).\n\nb(X) :- d(X).\n\nc(X, Y) :- b(X), p(X), b(Y).\n\nq(X) :- c(X, X).\n\nrel ok.\nok :- q(2).\n:- ok.\n"
  },
  {
    "path": "src/test/resources/test160_ok.flg",
    "content": "@edb rel a(i32).\n@edb rel d(i32).\n\n@topdown\nrel b(i32).\n\n@topdown\nrel c(i32, i32).\n\n@topdown\nrel p(i32).\n\n@bottomup\nrel q(i32).\n\na(0). a(1). a(2).\nd(0). d(2). d(4).\n\np(X) :- a(X).\n\nb(X) :- d(X).\n\nc(X, Y) :- b(X), p(X), b(Y).\n\nq(X) :- c(X, X).\n\nrel ok.\nok :- q(2).\n:- ok.\n"
  },
  {
    "path": "src/test/resources/test161_ok.flg",
    "content": "@edb rel a(i32).\n@edb rel d(i32).\n\n@bottomup\nrel b(i32).\n\n@bottomup\nrel c(i32, i32).\n\n@topdown\nrel p(i32).\n\n@topdown\nrel q(i32).\n\na(0). a(1). a(2).\nd(0). d(2). d(4).\n\np(X) :- a(X).\n\nb(X) :- d(X).\n\nc(X, Y) :- b(X), p(X), b(Y).\n\nq(X) :- c(X, X).\n\nrel ok.\nok :- q(2).\n:- ok.\n"
  },
  {
    "path": "src/test/resources/test162_ok.flg",
    "content": "@edb rel a(i32).\n@edb rel d(i32).\n\n@bottomup\nrel b(i32).\n\n@topdown\nrel c(i32, i32).\n\n@bottomup\nrel p(i32).\n\n@topdown\nrel q(i32).\n\na(0). a(1). a(2).\nd(0). d(2). d(4).\n\np(X) :- a(X).\n\nb(X) :- d(X).\n\nc(X, Y) :- b(X), p(X), b(Y).\n\nq(X) :- c(X, X).\n\nrel ok.\nok :- q(2).\n:- ok.\n"
  },
  {
    "path": "src/test/resources/test163_ok.flg",
    "content": "@edb rel a(i32).\n@edb rel d(i32).\n\n@bottomup\nrel b(i32).\n\n@topdown\nrel c(i32, i32).\n\n@topdown\nrel p(i32).\n\n@bottomup\nrel q(i32).\n\na(0). a(1). a(2).\nd(0). d(2). d(4).\n\np(X) :- a(X).\n\nb(X) :- d(X).\n\nc(X, Y) :- b(X), p(X), b(Y).\n\nq(X) :- c(X, X).\n\nrel ok.\nok :- q(2).\n:- ok.\n"
  },
  {
    "path": "src/test/resources/test164_ok.flg",
    "content": "@edb rel a(i32).\n@edb rel d(i32).\n\n@topdown\nrel b(i32).\n\n@bottomup\nrel c(i32, i32).\n\n@bottomup\nrel p(i32).\n\n@topdown\nrel q(i32).\n\na(0). a(1). a(2).\nd(0). d(2). d(4).\n\np(X) :- a(X).\n\nb(X) :- d(X).\n\nc(X, Y) :- b(X), p(X), b(Y).\n\nq(X) :- c(X, X).\n\nrel ok.\nok :- q(2).\n:- ok.\n"
  },
  {
    "path": "src/test/resources/test165_ok.flg",
    "content": "@edb rel a(i32).\n@edb rel d(i32).\n\n@topdown\nrel b(i32).\n\n@bottomup\nrel c(i32, i32).\n\n@topdown\nrel p(i32).\n\n@bottomup\nrel q(i32).\n\na(0). a(1). a(2).\nd(0). d(2). d(4).\n\np(X) :- a(X).\n\nb(X) :- d(X).\n\nc(X, Y) :- b(X), p(X), b(Y).\n\nq(X) :- c(X, X).\n\nrel ok.\nok :- q(2).\n:- ok.\n"
  },
  {
    "path": "src/test/resources/test166_ok.flg",
    "content": "@edb rel a(i32).\n@edb rel d(i32).\n\n@topdown\nrel b(i32).\n\n@topdown\nrel c(i32, i32).\n\n@bottomup\nrel p(i32).\n\n@bottomup\nrel q(i32).\n\na(0). a(1). a(2).\nd(0). d(2). d(4).\n\np(X) :- a(X).\n\nb(X) :- d(X).\n\nc(X, Y) :- b(X), p(X), b(Y).\n\nq(X) :- c(X, X).\n\nrel ok.\nok :- q(2).\n:- ok.\n"
  },
  {
    "path": "src/test/resources/test167_ok.flg",
    "content": "@edb rel a(i32).\n@edb rel d(i32).\n\n@bottomup\nrel b(i32).\n\n@bottomup\nrel c(i32, i32).\n\n@bottomup\nrel p(i32).\n\n@topdown\nrel q(i32).\n\na(0). a(1). a(2).\nd(0). d(2). d(4).\n\np(X) :- a(X).\n\nb(X) :- d(X).\n\nc(X, Y) :- b(X), p(X), b(Y).\n\nq(X) :- c(X, X).\n\nrel ok.\nok :- q(2).\n:- ok.\n"
  },
  {
    "path": "src/test/resources/test168_ok.flg",
    "content": "@edb rel a(i32).\n@edb rel d(i32).\n\n@bottomup\nrel b(i32).\n\n@bottomup\nrel c(i32, i32).\n\n@topdown\nrel p(i32).\n\n@bottomup\nrel q(i32).\n\na(0). a(1). a(2).\nd(0). d(2). d(4).\n\np(X) :- a(X).\n\nb(X) :- d(X).\n\nc(X, Y) :- b(X), p(X), b(Y).\n\nq(X) :- c(X, X).\n\nrel ok.\nok :- q(2).\n:- ok.\n"
  },
  {
    "path": "src/test/resources/test169_ok.flg",
    "content": "@edb rel a(i32).\n@edb rel d(i32).\n\n@bottomup\nrel b(i32).\n\n@topdown\nrel c(i32, i32).\n\n@bottomup\nrel p(i32).\n\n@bottomup\nrel q(i32).\n\na(0). a(1). a(2).\nd(0). d(2). d(4).\n\np(X) :- a(X).\n\nb(X) :- d(X).\n\nc(X, Y) :- b(X), p(X), b(Y).\n\nq(X) :- c(X, X).\n\nrel ok.\nok :- q(2).\n:- ok.\n"
  },
  {
    "path": "src/test/resources/test170_ok.flg",
    "content": "@edb rel a(i32).\n@edb rel d(i32).\n\n@topdown\nrel b(i32).\n\n@bottomup\nrel c(i32, i32).\n\n@bottomup\nrel p(i32).\n\n@bottomup\nrel q(i32).\n\na(0). a(1). a(2).\nd(0). d(2). d(4).\n\np(X) :- a(X).\n\nb(X) :- d(X).\n\nc(X, Y) :- b(X), p(X), b(Y).\n\nq(X) :- c(X, X).\n\nrel ok.\nok :- q(2).\n:- ok.\n"
  },
  {
    "path": "src/test/resources/test171_ok.flg",
    "content": "@edb rel a(i32).\n@edb rel d(i32).\n\n@bottomup\nrel b(i32).\n\n@bottomup\nrel c(i32, i32).\n\n@bottomup\nrel p(i32).\n\n@bottomup\nrel q(i32).\n\na(0). a(1). a(2).\nd(0). d(2). d(4).\n\np(X) :- a(X).\n\nb(X) :- d(X).\n\nc(X, Y) :- b(X), p(X), b(Y).\n\nq(X) :- c(X, X).\n\nrel ok.\nok :- q(2).\n:- ok.\n"
  },
  {
    "path": "src/test/resources/test172_ok.flg",
    "content": "@edb rel d(i32, i32).\n@edb rel e(i32, i32).\n@edb rel f(i32, i32).\n\n@bottomup\nrel a(i32, i32).\n@bottomup\nrel b(i32).\n\ne(1, 2).\nd(1, 2). d(3, 4). d(4, 5). d(5, 3).\nf(2, 1).\n\n(* Modified this slightly from example by Meskes and Noack [1993] *)\nb(X) :- e(Z, X), f(X, Z).\na(X, Y) :- d(X, Y), !b(Y).\na(X, Y) :- a(X, Z), d(Z, Y), !b(Y).\n\n@edb rel three(i32).\nthree(3).\n@edb rel four(i32).\nfour(4).\n@edb rel five(i32).\nfive(5).\n\nrel ok1.\nok1 :- a(3, X), three(X).\n\nrel ok2.\nok2 :- a(3, X), four(X).\n\nrel ok3.\nok3 :- a(3, X), five(X).\n\nrel ok.\nok :- ok1, ok2, ok3.\n"
  },
  {
    "path": "src/test/resources/test173_ok.flg",
    "content": "@edb rel d(i32, i32).\n@edb rel e(i32, i32).\n@edb rel f(i32, i32).\n\n@topdown\nrel a(i32, i32).\n@bottomup\nrel b(i32).\n\ne(1, 2).\nd(1, 2). d(3, 4). d(4, 5). d(5, 3).\nf(2, 1).\n\n(* Modified this slightly from example by Meskes and Noack [1993] *)\nb(X) :- e(Z, X), f(X, Z).\na(X, Y) :- d(X, Y), !b(Y).\na(X, Y) :- a(X, Z), d(Z, Y), !b(Y).\n\n@edb rel three(i32).\nthree(3).\n@edb rel four(i32).\nfour(4).\n@edb rel five(i32).\nfive(5).\n\nrel ok1.\nok1 :- a(3, X), three(X).\n\nrel ok2.\nok2 :- a(3, X), four(X).\n\nrel ok3.\nok3 :- a(3, X), five(X).\n\nrel ok.\nok :- ok1, ok2, ok3.\n"
  },
  {
    "path": "src/test/resources/test174_ok.flg",
    "content": "@edb rel d(i32, i32).\n@edb rel e(i32, i32).\n@edb rel f(i32, i32).\n\n@bottomup\nrel a(i32, i32).\n@topdown\nrel b(i32).\n\ne(1, 2).\nd(1, 2). d(3, 4). d(4, 5). d(5, 3).\nf(2, 1).\n\n(* Modified this slightly from example by Meskes and Noack [1993] *)\nb(X) :- e(Z, X), f(X, Z).\na(X, Y) :- d(X, Y), !b(Y).\na(X, Y) :- a(X, Z), d(Z, Y), !b(Y).\n\n@edb rel three(i32).\nthree(3).\n@edb rel four(i32).\nfour(4).\n@edb rel five(i32).\nfive(5).\n\nrel ok1.\nok1 :- a(3, X), three(X).\n\nrel ok2.\nok2 :- a(3, X), four(X).\n\nrel ok3.\nok3 :- a(3, X), five(X).\n\nrel ok.\nok :- ok1, ok2, ok3.\n"
  },
  {
    "path": "src/test/resources/test175_ok.flg",
    "content": "@edb rel d(i32, i32).\n@edb rel e(i32, i32).\n@edb rel f(i32, i32).\n\n@topdown\nrel a(i32, i32).\n@topdown\nrel b(i32).\n\ne(1, 2).\nd(1, 2). d(3, 4). d(4, 5). d(5, 3).\nf(2, 1).\n\n(* Modified this slightly from example by Meskes and Noack [1993] *)\nb(X) :- e(Z, X), f(X, Z).\na(X, Y) :- d(X, Y), !b(Y).\na(X, Y) :- a(X, Z), d(Z, Y), !b(Y).\n\n@edb rel three(i32).\nthree(3).\n@edb rel four(i32).\nfour(4).\n@edb rel five(i32).\nfive(5).\n\nrel ok1.\nok1 :- a(3, X), three(X).\n\nrel ok2.\nok2 :- a(3, X), four(X).\n\nrel ok3.\nok3 :- a(3, X), five(X).\n\nrel ok.\nok :- ok1, ok2, ok3.\n"
  },
  {
    "path": "src/test/resources/test176_ok.flg",
    "content": "@edb rel d(i32, i32).\n@edb rel e(i32, i32).\n@edb rel f(i32, i32).\n\n@bottomup\nrel a(i32, i32).\n@bottomup\nrel b(i32).\n\ne(1, 2).\nd(1, 2). d(3, 4). d(4, 5). d(5, 3).\nf(2, 1).\n\n(* Modified this slightly from example by Meskes and Noack [1993] *)\nb(X) :- e(Z, X), f(X, Z).\na(X, Y) :- d(X, Y), !b(Y).\na(X, Y) :- a(X, Z), d(Z, Y), !b(Y).\n\n@edb rel three(i32).\nthree(3).\n@edb rel four(i32).\nfour(4).\n@edb rel five(i32).\nfive(5).\n\nrel ok1.\nok1 :- a(3, X), three(X).\n\nrel ok2.\nok2 :- a(3, X), four(X).\n\nrel ok3.\nok3 :- a(3, X), five(X).\n\nrel ok.\nok :- ok1, ok2, ok3.\n:- ok.\n"
  },
  {
    "path": "src/test/resources/test177_ok.flg",
    "content": "@edb rel d(i32, i32).\n@edb rel e(i32, i32).\n@edb rel f(i32, i32).\n\n@topdown\nrel a(i32, i32).\n@bottomup\nrel b(i32).\n\ne(1, 2).\nd(1, 2). d(3, 4). d(4, 5). d(5, 3).\nf(2, 1).\n\n(* Modified this slightly from example by Meskes and Noack [1993] *)\nb(X) :- e(Z, X), f(X, Z).\na(X, Y) :- d(X, Y), !b(Y).\na(X, Y) :- a(X, Z), d(Z, Y), !b(Y).\n\n@edb rel three(i32).\nthree(3).\n@edb rel four(i32).\nfour(4).\n@edb rel five(i32).\nfive(5).\n\nrel ok1.\nok1 :- a(3, X), three(X).\n\nrel ok2.\nok2 :- a(3, X), four(X).\n\nrel ok3.\nok3 :- a(3, X), five(X).\n\nrel ok.\nok :- ok1, ok2, ok3.\n:- ok.\n"
  },
  {
    "path": "src/test/resources/test178_ok.flg",
    "content": "@edb rel d(i32, i32).\n@edb rel e(i32, i32).\n@edb rel f(i32, i32).\n\n@bottomup\nrel a(i32, i32).\n@topdown\nrel b(i32).\n\ne(1, 2).\nd(1, 2). d(3, 4). d(4, 5). d(5, 3).\nf(2, 1).\n\n(* Modified this slightly from example by Meskes and Noack [1993] *)\nb(X) :- e(Z, X), f(X, Z).\na(X, Y) :- d(X, Y), !b(Y).\na(X, Y) :- a(X, Z), d(Z, Y), !b(Y).\n\n@edb rel three(i32).\nthree(3).\n@edb rel four(i32).\nfour(4).\n@edb rel five(i32).\nfive(5).\n\nrel ok1.\nok1 :- a(3, X), three(X).\n\nrel ok2.\nok2 :- a(3, X), four(X).\n\nrel ok3.\nok3 :- a(3, X), five(X).\n\nrel ok.\nok :- ok1, ok2, ok3.\n:- ok.\n"
  },
  {
    "path": "src/test/resources/test179_ok.flg",
    "content": "@edb rel d(i32, i32).\n@edb rel e(i32, i32).\n@edb rel f(i32, i32).\n\n@topdown\nrel a(i32, i32).\n@topdown\nrel b(i32).\n\ne(1, 2).\nd(1, 2). d(3, 4). d(4, 5). d(5, 3).\nf(2, 1).\n\n(* Modified this slightly from example by Meskes and Noack [1993] *)\nb(X) :- e(Z, X), f(X, Z).\na(X, Y) :- d(X, Y), !b(Y).\na(X, Y) :- a(X, Z), d(Z, Y), !b(Y).\n\n@edb rel three(i32).\nthree(3).\n@edb rel four(i32).\nfour(4).\n@edb rel five(i32).\nfive(5).\n\nrel ok1.\nok1 :- a(3, X), three(X).\n\nrel ok2.\nok2 :- a(3, X), four(X).\n\nrel ok3.\nok3 :- a(3, X), five(X).\n\nrel ok.\nok :- ok1, ok2, ok3.\n:- ok.\n"
  },
  {
    "path": "src/test/resources/test180_ok.flg",
    "content": "@edb rel foo(bool).\nfoo(true).\n\nrel ok.\nok :-\n  if true then true else false,\n  let X = true in X && X,\n  match true with | true => true | false => false end,\n  foo(if true then true else false),\n  foo(let X = true in X && X),\n  foo(match true with | true => true | false => false end)."
  },
  {
    "path": "src/test/resources/test181_ok.flg",
    "content": "@edb rel foo(bool)\nfoo(true).\n\nrel ok.\nok :-\n  F = false,\n  T = true,\n  if T then T else F,\n  match true with X => X && T end,\n  let X = true in X && T,\n  match T with | true => T | false => F end,\n  foo(if T then T else F),\n  foo(let X = true in X && T),\n  foo(match T with | true => T | false => F end).\n"
  },
  {
    "path": "src/test/resources/test182_ok.flg",
    "content": "type 'a linked_list = {\n  val : 'a ;\n  next : 'a linked_list option ;\n}.\n\nrel ok.\nok :-\n\tX = { val = 42 ; next = none ; },\n\tY = { val = 21 ; next = some(X) ; },\n\tsome(Z) = next(Y),\n\t42 = val(Z)."
  },
  {
    "path": "src/test/resources/test183_ok.flg",
    "content": "type 'a linked_list = {\n  val : 'a ;\n  next : 'a linked_list option ;\n}.\n\nrel ok.\nok :-\n\tY = { val = 21 ; next = some ({ val = 42 ; next = none }) },\n\tsome(Z) = next(Y),\n\t42 = val(Z)."
  },
  {
    "path": "src/test/resources/test184_ok.flg",
    "content": "type 'a linked_list = {\n  val : 'a ;\n  next : 'a linked_list option ;\n}.\n\nrel ok.\nok :-\n\tX = { val = 42 ; next = none ; },\n\tY = { val = \"hello\" ; next = none ; },\n\tnext(X) = none,\n\tnext(Y) = none."
  },
  {
    "path": "src/test/resources/test185_bd.flg",
    "content": "type 'a linked_list = {\n  val : 'a ;\n  next : 'a linked_list option ;\n}.\n\nrel ok.\nok :-\n\tX = { val = 42 ; next = none ; },\n\tY = { val = \"hello\" ; next = some(X) ; },\n\tsome(Z) = next(Y),\n\t42 = val(Z)."
  },
  {
    "path": "src/test/resources/test186_ok.flg",
    "content": "type 'a linked_list = {\n  val : 'a ;\n  next : 'a linked_list option ;\n}.\n\nrel ok.\nok :-\n\tX = { val = 21 ; next = some({ val = 33 ; next = none }) ; },\n\tY = { X with next = some({ val = 42 ; next = none }) },\t\n\tsome(Z) = next(Y),\n\t42 = val(Z)."
  },
  {
    "path": "src/test/resources/test187_ok.flg",
    "content": "type 'a linked_list = {\n  val : 'a ;\n  next : 'a linked_list option ;\n}.\n\nrel ok.\nok :-\n\t_X = { val = 21 ; next = some({ val = 33 ; next = none }) ; },\n\tY = { _X with next = some({ val = 42 ; next = none }) ; val = 0 },\t\n\tsome(Z) = next(Y),\n\t42 = val(Z),\n\t0 = val(Y).\n"
  },
  {
    "path": "src/test/resources/test188_bd.flg",
    "content": "type 'a linked_list = {\n  val : 'a ;\n  next : 'a linked_list option ;\n}.\n\nrel ok.\nok :-\n\tX = { val = 21 ; next = some({ val = 33 ; next = none }) ; },\n\tY = { X with next = some({ val = \"hello\" ; next = none }) },\t\n\tsome(Z) = next(Y),\n\t42 = val(Z)."
  },
  {
    "path": "src/test/resources/test189_ok.flg",
    "content": "type point2d = {\n  x : i32;\n  y : i32;\n}.\n\nrel ok.\nok :-\n  P = #p[point2d],\n  E = `#x(P) #= #y(P) /\\ #x(P) #= 0`,\n  is_sat(E),\n  F = `#y(P) #= 42`,\n  is_sat(F),\n  !is_sat(`E /\\ F`)."
  },
  {
    "path": "src/test/resources/test190_ok.flg",
    "content": "type point2d = {\n  x : i32;\n  y : i32;\n}.\n\nrel ok.\nok :-\n  P = #p[point2d],\n  E = [`#x(P) #= #y(P)`, `#x(P) #= 0`],\n  some(M) = get_model(E, none),\n  some(P2) = query_model(P, M),\n  y(P2) = 0.\n"
  },
  {
    "path": "src/test/resources/test191_input/complex_terms.tsv",
    "content": "my_cons(\"jimmy\", my_nil)\tmy_cons(\"hendrix\", my_nil)\nmy_cons(\"giuseppe\", my_nil)\tmy_cons(\"garibaldi\", my_nil)\n"
  },
  {
    "path": "src/test/resources/test191_input/names.tsv",
    "content": "\"jimmy\"\t\"hendrix\"\n\"giuseppe\"\t\"garibaldi\"\n"
  },
  {
    "path": "src/test/resources/test191_input/numbers.tsv",
    "content": "1\t2\t3\n4\t5\t6\n7\t8\t9\n"
  },
  {
    "path": "src/test/resources/test191_ok.flg",
    "content": "@disk\n@edb rel names(string, string).\n\n@disk\n@edb rel numbers(i32, i32, i32).\nnumbers(100, 200, 300).\n\ntype 'a my_list =\n  | my_nil\n  | my_cons('a, 'a my_list).\n\n@disk\n@edb rel complex_terms(string my_list, string my_list).\n\nrel ok1.\nok1 :-\n  names(\"jimmy\", \"hendrix\"),\n  names(\"giuseppe\", \"garibaldi\").\n\nfun singleton(X: 'a) : 'a my_list = my_cons(X, my_nil).\n\nrel ok2.\nok2 :-\n  complex_terms(singleton(\"jimmy\"), singleton(\"hendrix\")),\n  complex_terms(singleton(\"giuseppe\"), singleton(\"garibaldi\")).\n\nrel ok3.\nok3 :-\n  numbers(1, 2, 3),\n  numbers(4, 5, 6),\n  numbers(7, 8, 9),\n  numbers(100, 200, 300).\n\nrel ok.\nok :- ok1, ok2, ok3.\n"
  },
  {
    "path": "src/test/resources/test192_ok.flg",
    "content": "rel ok.\nok :-\n\tX = #{\"x\"}[i32],\n\tY = #{42}[i32],\n\tZ = #{(\"x\", 42)}[i32],\n\tX != Y,\n\tX != Z,\n\tY != X,\n\tY != Z,\n\tZ != X,\n\tZ != Y."
  },
  {
    "path": "src/test/resources/test193_ok.flg",
    "content": "@edb rel nums(i32, i32, i32)\nnums(1, 2, 3).\nnums(4, 3, 1).\nnums(1, 3, 6).\n\n@edb rel num(i32)\nnum(6).\nnum(12).\nnum(18).\n\n@edb rel empty\n\n@edb rel full\nfull.\n\nfun sum(Xs: i32 list) : i32 =\n  match Xs with\n  | [] => 0\n  | X :: Xs => X + sum(Xs)\n  end\n\nfun mulAndSum(Xs: (i32 * i32 * i32) list) : i32 =\n  match Xs with\n  | [] => 0\n  | (X1, X2, X3) :: Xs => X1 * X2 * X3 + mulAndSum(Xs)\n  end\n\nrel agg1(i32)\n\nagg1(X) :- X = sum(nums(_1, ??, _2)).\n\nrel agg2(i32)\n\nagg2(X) :- X = sum(nums(_1, 3, ??)).\n\nrel ok\nok :-\n  sum(num(??)) = 36,\n  mulAndSum(nums(??, ??, ??)) = 36,\n  agg1(8),\n  agg2(7),\n  !empty,\n  full.\n"
  },
  {
    "path": "src/test/resources/test217_bd.flg",
    "content": "@topdown\nrel foo\nrel bar\n\nfun f : bool = foo\n\nbar :- f."
  },
  {
    "path": "src/test/resources/test218_bd.flg",
    "content": "rel foo\nrel bar\n\nfun f : bool = foo\n\nbar :- f.\n\n:- bar."
  },
  {
    "path": "src/test/resources/test219_ok.flg",
    "content": "@bottomup\nrel foo\nrel bar\n\nfun f : bool = foo\n\nbar :- f.\n\n:- bar."
  },
  {
    "path": "src/test/resources/test220_ok.flg",
    "content": "rel p\nrel q\nrel r1\nrel r2\nrel r3\nrel s\n\np :- !q, r1, r2, r3.\nq :- s.\nr1 :- p.\nr2 :- p.\nr3 :- p.\ns :- p.\nr1.\nr2.\nr3.\ns.\n\nrel ok\nok :- p.\n"
  },
  {
    "path": "src/test/resources/test221_ok.flg",
    "content": "rel p\nrel q\nrel r1\nrel r2\nrel r3\nrel s\n\np :- r1, !q, r2, r3.\nq :- s.\nr1 :- p.\nr2 :- p.\nr3 :- p.\ns :- p.\nr1.\nr2.\nr3.\ns.\n\nrel ok\nok :- p.\n"
  },
  {
    "path": "src/test/resources/test222_ok.flg",
    "content": "rel p\nrel q\nrel r1\nrel r2\nrel r3\nrel s\n\np :- r1, r2, !q, r3.\nq :- s.\nr1 :- p.\nr2 :- p.\nr3 :- p.\ns :- p.\nr1.\nr2.\nr3.\ns.\n\nrel ok\nok :- p.\n"
  },
  {
    "path": "src/test/resources/test223_ok.flg",
    "content": "rel p\nrel q\nrel r1\nrel r2\nrel r3\nrel s\n\np :- r1, r2, r3, !q.\nq :- s.\nr1 :- p.\nr2 :- p.\nr3 :- p.\ns :- p.\nr1.\nr2.\nr3.\ns.\n\nrel ok\nok :- p.\n"
  },
  {
    "path": "src/test/resources/test224_ok.flg",
    "content": "rel p\nrel q\nrel r1\nrel r2\nrel r3\nrel s\n\np :- !q, r1, r2, r3.\nq :- s.\nr1 :- p.\nr2 :- p.\nr3 :- p.\ns :- p.\nr1.\nr2.\nr3.\ns.\n\nrel ok\nok :- p.\n\n:- ok."
  },
  {
    "path": "src/test/resources/test225_ok.flg",
    "content": "rel p\nrel q\nrel r1\nrel r2\nrel r3\nrel s\n\np :- r1, !q, r2, r3.\nq :- s.\nr1 :- p.\nr2 :- p.\nr3 :- p.\ns :- p.\nr1.\nr2.\nr3.\ns.\n\nrel ok\nok :- p.\n\n:- ok."
  },
  {
    "path": "src/test/resources/test226_ok.flg",
    "content": "rel p\nrel q\nrel r1\nrel r2\nrel r3\nrel s\n\np :- r1, r2, !q, r3.\nq :- s.\nr1 :- p.\nr2 :- p.\nr3 :- p.\ns :- p.\nr1.\nr2.\nr3.\ns.\n\nrel ok\nok :- p.\n\n:- ok."
  },
  {
    "path": "src/test/resources/test227_ok.flg",
    "content": "rel p\nrel q\nrel r1\nrel r2\nrel r3\nrel s\n\np :- r1, r2, r3, !q.\nq :- s.\nr1 :- p.\nr2 :- p.\nr3 :- p.\ns :- p.\nr1.\nr2.\nr3.\ns.\n\nrel ok\nok :- p.\n\n:- ok."
  },
  {
    "path": "src/test/resources/test228_ok.flg",
    "content": "rel p\nrel q\nrel r1\nrel r2\nrel r3\nrel s\n\np :- !q, r1, r2, r3.\nq :- p.\nr1 :- p.\nr2 :- p.\nr3 :- p.\ns :- p.\nr1 :- s.\nr2 :- s.\nr3 :- s.\ns.\nq.\n\nrel ok\nok :- !p.\n"
  },
  {
    "path": "src/test/resources/test229_ok.flg",
    "content": "rel p\nrel q\nrel r1\nrel r2\nrel r3\nrel s\n\np :- r1, !q, r2, r3.\nq :- p.\nr1 :- p.\nr2 :- p.\nr3 :- p.\ns :- p.\nr1 :- s.\nr2 :- s.\nr3 :- s.\ns.\nq.\n\nrel ok\nok :- !p.\n"
  },
  {
    "path": "src/test/resources/test230_ok.flg",
    "content": "rel p\nrel q\nrel r1\nrel r2\nrel r3\nrel s\n\np :- r1, r2, !q, r3.\nq :- p.\nr1 :- p.\nr2 :- p.\nr3 :- p.\ns :- p.\nr1 :- s.\nr2 :- s.\nr3 :- s.\ns.\nq.\n\nrel ok\nok :- !p.\n"
  },
  {
    "path": "src/test/resources/test231_ok.flg",
    "content": "rel p\nrel q\nrel r1\nrel r2\nrel r3\nrel s\n\np :- r1, r2, r3, !q.\nq :- p.\nr1 :- p.\nr2 :- p.\nr3 :- p.\ns :- p.\nr1 :- s.\nr2 :- s.\nr3 :- s.\ns.\nq.\n\nrel ok\nok :- !p.\n"
  },
  {
    "path": "src/test/resources/test232_ok.flg",
    "content": "rel p\nrel q\nrel r1\nrel r2\nrel r3\nrel s\n\np :- !q, r1, r2, r3.\nq :- p.\nr1 :- p.\nr2 :- p.\nr3 :- p.\ns :- p.\nr1 :- s.\nr2 :- s.\nr3 :- s.\ns.\nq.\n\nrel ok\nok :- !p.\n\n:- ok."
  },
  {
    "path": "src/test/resources/test233_ok.flg",
    "content": "rel p\nrel q\nrel r1\nrel r2\nrel r3\nrel s\n\np :- r1, !q, r2, r3.\nq :- p.\nr1 :- p.\nr2 :- p.\nr3 :- p.\ns :- p.\nr1 :- s.\nr2 :- s.\nr3 :- s.\ns.\nq.\n\nrel ok\nok :- !p.\n\n:- ok."
  },
  {
    "path": "src/test/resources/test234_ok.flg",
    "content": "rel p\nrel q\nrel r1\nrel r2\nrel r3\nrel s\n\np :- r1, r2, !q, r3.\nq :- p.\nr1 :- p.\nr2 :- p.\nr3 :- p.\ns :- p.\nr1 :- s.\nr2 :- s.\nr3 :- s.\ns.\nq.\n\nrel ok\nok :- !p.\n\n:- ok."
  },
  {
    "path": "src/test/resources/test235_ok.flg",
    "content": "rel p\nrel q\nrel r1\nrel r2\nrel r3\nrel s\n\np :- r1, r2, r3, !q.\nq :- p.\nr1 :- p.\nr2 :- p.\nr3 :- p.\ns :- p.\nr1 :- s.\nr2 :- s.\nr3 :- s.\ns.\nq.\n\nrel ok\nok :- !p.\n\n:- ok."
  },
  {
    "path": "src/test/resources/test236_ok.flg",
    "content": "rel q.\nq :- !q.\n\nrel ok.\nok :- q.\n"
  },
  {
    "path": "src/test/resources/test237_ok.flg",
    "content": "rel q.\nq :- !q.\n\nrel ok.\nok :- q.\n\n:- ok.\n"
  },
  {
    "path": "src/test/resources/test238_ok.flg",
    "content": "@edb rel a(i32, i32)\n@edb rel b(i32, i32)\nrel p(i32, i32)\n\na(1, 2).\na(1, 3).\n\np(X, Y) :- a(X, Y).\n\nrel not_ok\nnot_ok :- !a(_, _).\nnot_ok :- !a(_, 2).\nnot_ok :- !a(_, 3).\nnot_ok :- !p(_, _).\nnot_ok :- !p(_, 2).\nnot_ok :- !p(_, 3).\n\nrel ok\nok :-\n  !b(_, _),\n  !a(2, _),\n  !p(2, _),\n  !not_ok.\n"
  },
  {
    "path": "src/test/resources/test240_ok.flg",
    "content": "@edb rel foo(i32 smt)\n\nfoo(`bv_to_bv_unsigned[64,32](0L)`).\n\nrel ok\nok :- foo(X), is_valid(`X #= 0`).\n"
  },
  {
    "path": "src/test/resources/test241_ok.flg",
    "content": "@edb rel foo(i32 smt)\n\nfoo(let X = `bv_to_bv_unsigned[64,32](0L)` in X).\n\nrel ok\nok :- foo(X), is_valid(`X #= 0`).\n"
  },
  {
    "path": "src/test/resources/test242_ok.flg",
    "content": "rel ok\nok :- Y = `bv_to_bv_unsigned[64,32](0L)`, is_valid(`Y #= 0`).\n"
  },
  {
    "path": "src/test/resources/test243_ok.flg",
    "content": "rel ok\nok :- Y = let X = `bv_to_bv_unsigned[64,32](0L)` in X, is_valid(`Y #= 0`).\n"
  },
  {
    "path": "src/test/resources/test244_ok.flg",
    "content": "fun foo : i32 smt = `bv_to_bv_unsigned[64,32](0L)`\n\nrel ok\nok :- foo = X, is_valid(`X #= 0`).\n"
  },
  {
    "path": "src/test/resources/test245_ok.flg",
    "content": "fun foo : i32 smt = let X = `bv_to_bv_unsigned[64,32](0L)` in X\n\nrel ok\nok :- foo = X, is_valid(`X #= 0`).\n"
  },
  {
    "path": "src/test/resources/test248_bd.flg",
    "content": "fun foo(X: i32) : i32 = X\n\nfun bar : i32 = foo(_X)\n"
  },
  {
    "path": "src/test/resources/test249_ok.flg",
    "content": "@edb rel ok\nok.\n:- ok."
  },
  {
    "path": "src/test/resources/test250_ok.flg",
    "content": "@topdown\nrel foo(i32)\n\nfoo(0).\nfoo(42).\n\nrel ok\nok :- foo(42), foo(_)."
  },
  {
    "path": "src/test/resources/test251_bd.flg",
    "content": "@topdown\nrel foo(i32)\nfoo(_X).\n\nfun bar(X: i32) : bool = foo(X)\n"
  },
  {
    "path": "src/test/resources/test252_ok.flg",
    "content": "@bottomup\nrel foo(i32)\nfoo(42).\n\nfun bar(X: i32) : bool = foo(X)\n\nrel ok\nok :- bar(42).\n:- ok."
  },
  {
    "path": "src/test/resources/test253_ok.flg",
    "content": "rel foo(i32)\nfoo(42).\n\nfun bar(X: i32) : bool = foo(X)\n\n@topdown\nrel baz(i32)\nbaz(X) :- foo(X).\n\nrel ok\nok :- baz(42)."
  },
  {
    "path": "src/test/resources/test254_ok.flg",
    "content": "rel ok\nok :-\n\tX = #x[string],\n\tsome(_) = get_model([`X #= \"!@#$%^&*-+/\"`], none).\n"
  },
  {
    "path": "src/test/resources/test255_bd.flg",
    "content": "fun foo(X: 'a list) : 'a list =\n  match X with\n  | X :: X :: _ => [X, X]\n  | _ => []\n  end"
  },
  {
    "path": "src/test/resources/test256_ok.flg",
    "content": "(*\n  Test to make sure that dependencies in the heads of rules are being picked up.\n*)\n\nrel bar\n\nbar.\n\nrel baz\n\nbaz.\n\nrel woo\n\nwoo.\n\nrel foo1(bool)\nrel foo2(bool)\nrel foo3(bool)\nrel foo4(bool)\nrel foo5(bool)\n\nfoo1(bar && baz && woo).\nfoo2(bar && baz && woo).\nfoo3(bar && baz && woo).\nfoo4(bar && baz && woo).\nfoo5(bar && baz && woo).\n\nrel ok\nok :-\n  foo1(true),\n  foo2(true),\n  foo3(true),\n  foo4(true),\n  foo5(true),\n  true."
  },
  {
    "path": "src/test/resources/test257_ok.flg",
    "content": "@edb rel foo(string)\n\nfoo(\"\\n\").\nfoo(\"\\r\").\nfoo(\"\\t\").\nfoo(\"\\\"\").\nfoo(\"\\\\\").\nfoo(\"\\\\\\\"\").\nfoo(\"\\\"\\\\\").\nfoo(\"\\\\\\\"\\\\\").\nfoo(\"\\\"\\\\\\\"\").\n\n@edb rel bar(string list)\n\nbar([\"\\\"\", \"\\\\\"]).\nbar([\"\\\\\", \"\\\"\"]).\nbar([\"\\\"\", \"\\\\\", \"\\\"\"]).\nbar([\"\\\\\", \"\\\"\", \"\\\\\"])."
  },
  {
    "path": "src/test/resources/test258_ok.flg",
    "content": "(*\n  This is a weird test... basically trying to make sure that the parser rejects\n  the string below. If the parser does so, it apparently skips that line, and so\n  `foo` is empty.\n*)\n\n@edb rel foo(string)\n\nfoo(\"\\\\\"\").\n\nrel ok\nok :- !foo(_)."
  },
  {
    "path": "src/test/resources/test259_ok.flg",
    "content": "@topdown\nrel mem(i32, i32 list)\n\nmem(X, X :: _).\nmem(X, _ :: Xs) :- mem(X, Xs).\n\nrel ok.\n\nok :- mem(X, [1, 2, 3]), X = 3.\n"
  },
  {
    "path": "src/test/resources/test260_ok.flg",
    "content": "rel foo\nrel ok\nok :- !foo."
  },
  {
    "path": "src/test/resources/test261_ok.flg",
    "content": "@edb rel foo(fp32)\nfoo(1e-1f).\nfoo(1.0e-2f).\nfoo(1e1f).\nfoo(1.0e2f).\n\nrel ok.\nok :-\n  foo(0.01f),\n  foo(0.1f),\n  foo(10f),\n  foo(100f)."
  },
  {
    "path": "src/test/resources/test262_ok.flg",
    "content": "@edb rel foo(fp32)\nfoo(1e-1F).\nfoo(1.0e-2F).\nfoo(1e1F).\nfoo(1.0e2F).\n\nrel ok.\nok :-\n  foo(0.01F),\n  foo(0.1F),\n  foo(10F),\n  foo(100F)."
  },
  {
    "path": "src/test/resources/test263_ok.flg",
    "content": "@edb rel foo(fp64)\nfoo(1e-1d).\nfoo(1.0e-2d).\nfoo(1e1d).\nfoo(1.0e2d).\n\nrel ok.\nok :-\n  foo(0.01d),\n  foo(0.1d),\n  foo(10d),\n  foo(100d)."
  },
  {
    "path": "src/test/resources/test264_ok.flg",
    "content": "@edb rel foo(fp64)\nfoo(1e-1D).\nfoo(1.0e-2D).\nfoo(1e1D).\nfoo(1.0e2D).\n\nrel ok.\nok :-\n  foo(0.01D),\n  foo(0.1D),\n  foo(10D),\n  foo(100D)."
  },
  {
    "path": "src/test/resources/test265_ok.flg",
    "content": "@edb rel foo(fp64)\nfoo(1e-1).\nfoo(1.0e-2).\nfoo(1e1).\nfoo(1.0e2).\n\nrel ok.\nok :-\n  foo(0.01),\n  foo(0.1),\n  foo(10d),\n  foo(100D)."
  },
  {
    "path": "src/test/resources/test266_bd.flg",
    "content": "rel foo(i32 list)\n\nrel ok.\n\nok :- _X = foo([??])."
  },
  {
    "path": "src/test/resources/test267_ok.flg",
    "content": "rel ok.\n\nok :- match (true, false) with (_, _) => true end."
  },
  {
    "path": "src/test/resources/test268_bd.flg",
    "content": "fun make_eq(X : 'a list smt, Y : 'a) : bool smt = `#cons_1(#cons_2(X)) #= Y`.\n\nrel ok.\nok :-\n  X1 = #x[i32],\n  L1 = `[1, X1, 2]`,\n  X2 = #x[string],\n  L2 = `[\"foo\", X2, \"baz\"]`,\n  E = make_eq(L1, 42),\n  F = make_eq(L2, \"bar\"),\n  is_sat(E),\n  is_sat(F),\n  is_sat(`E /\\ F`).\n"
  },
  {
    "path": "src/test/resources/test269_bd.flg",
    "content": "rel ok.\nok :- is_valid(`[] #= []`).\n"
  },
  {
    "path": "src/test/resources/test270_bd.flg",
    "content": "rel ok.\nok :- is_valid(`smt_eq['a list list]([], [])`).\n"
  },
  {
    "path": "src/test/resources/test271_bd.flg",
    "content": "rel ok.\nok :- is_valid(`smt_eq[i32 smt list list]([], [])`).\n"
  },
  {
    "path": "src/test/resources/test272_bd.flg",
    "content": "rel ok.\nok :- is_valid(`smt_eq[i32 sym list list]([], [])`).\n"
  },
  {
    "path": "src/test/resources/test273_ok.flg",
    "content": "rel ok\n\nok :- is_sat(`(4 #= 42) #= (\"hello\" #= \"goodbye\")`). \n"
  },
  {
    "path": "src/test/resources/test274_ok.flg",
    "content": "@edb rel phi(bool smt)\n\nphi(`false #= true`).\nphi(`\"hello\" #= \"hello\"`).\n\nrel ok\n\nok :- phi(`X #= _Y`), is_valid(`~X`)."
  },
  {
    "path": "src/test/resources/test275_ok.flg",
    "content": "@edb rel phi(bool smt)\n\nphi(`false #= true`).\nphi(`\"hello\" #= \"hello\"`).\n\nrel ok\n\nok :- phi(`smt_eq[bool](X, _Y)`), is_valid(`~X`)."
  },
  {
    "path": "src/test/resources/test276_inputA/foo.tsv",
    "content": "0\n"
  },
  {
    "path": "src/test/resources/test276_inputB/foo.tsv",
    "content": "42\n"
  },
  {
    "path": "src/test/resources/test276_ok.flg",
    "content": "@disk\n@edb rel foo(i32) \n\nrel ok\n\nok :- foo(0), foo(42)."
  },
  {
    "path": "src/test/resources/test277_ok.flg",
    "content": "rel test(i32) \n\ntest(1) :- string_matches(\"hello\", \"hello\").\ntest(2) :- string_matches(\"hello\", \".*el.*\").\ntest(3) :- string_matches(\"hello\", \"he.*o\").\ntest(4) :- string_matches(\"hello\", \"^h.*o\").\ntest(5) :- string_matches(\"hello\", \"h.*o$\").\ntest(6) :- !string_matches(\"hello\", \"^e.*o\").\ntest(7) :- !string_matches(\"hello\", \"h.*l$\").\ntest(8) :- string_starts_with(\"hello\", \"\").\ntest(9) :- string_starts_with(\"hello\", \"he\").\ntest(10) :- string_starts_with(\"hello\", \"hello\").\ntest(11) :- !string_starts_with(\"hello\", \"hello!\").\ntest(12) :- !string_starts_with(\"hello\", \"el\").\n\nrel ok\n\nok :-\n  test(1),\n  test(2),\n  test(3),\n  test(4),\n  test(5),\n  test(6),\n  test(7),\n  test(8),\n  test(9),\n  test(10),\n  test(11),\n  test(12)."
  },
  {
    "path": "src/test/resources/test278_ok.flg",
    "content": "@edb rel foo(bool sym)\nfoo(#{41}[bool]).\nfoo(#hello[bool]).\n\nrel ok\nok :- foo(#{X}[bool]), X + 1 = 42.\n"
  },
  {
    "path": "src/test/resources/test279_ok.flg",
    "content": "fun foo(X: i32) : i32 =\n  let fun bar(Y: i32) : i32 = Y + X in\n  bar(3)\n\nrel ok\nok :- foo(2) = 5.\n"
  },
  {
    "path": "src/test/resources/test280_ok.flg",
    "content": "fun foo(X1: i32, X2: i32) : i32 =\n  let fun bar(Y: i32) : i32 = Y + X1 in\n  let fun baz(Z: i32) : i32 = bar(Z) + X2 in\n  baz(3)\n\nrel ok\nok :- foo(1, 2) = 6.\n"
  },
  {
    "path": "src/test/resources/test281_ok.flg",
    "content": "fun foo(X1: i32, X2: i32) : i32 =\n  let fun bar(Z: i32) : i32 = Z * X1 * baz(0)\n      and baz(Z: i32) : i32 = if Z = 0 then 1 else X2 * bar(Z) in\n  baz(3)\n\nrel ok\nok :- foo(2, 4) = 24.\n"
  },
  {
    "path": "src/test/resources/test282_ok.flg",
    "content": "fun foo(X1: i32, X2: i32) : i32 =\n  let fun bar(Z: i32) : i32 = Z * X1 * baz(0, 1)\n      and baz(Z: i32, X1: i32) : i32 = if Z = 0 then X1 else X2 * bar(Z) in\n  baz(3, 1)\n\nrel ok\nok :- foo(2, 4) = 24.\n"
  },
  {
    "path": "src/test/resources/test283_ok.flg",
    "content": "fun sum(X: i32, Y: i32) : i32 = X + Y\nfun foo(Xs: i32 list) : i32 = fold[sum](0, Xs)\n\nrel ok\nok :- foo([1, 2, 3]) = 6."
  },
  {
    "path": "src/test/resources/test284_ok.flg",
    "content": "fun add1(Xs: i32 list, Y: i32) : i32 list = (Y + 1) :: Xs\nfun foo(Xs: i32 list) : i32 list = fold[add1]([], Xs)\n\nrel ok\nok :- foo([1, 2, 3]) = [4, 3, 2].\n"
  },
  {
    "path": "src/test/resources/test285_ok.flg",
    "content": "fun add1(Xs: i32 list, Y: i32) : i32 list = (Y + 1) :: Xs\nfun cons_wrapper(Xs: 'a list, X: 'a) : 'a list = X :: Xs\nfun foo(Xs: i32 list) : i32 list = fold[cons_wrapper]([], fold[add1]([], Xs))\n\nrel ok\nok :- foo([1, 2, 3]) = [2, 3, 4].\n"
  },
  {
    "path": "src/test/resources/test286_ok.flg",
    "content": "fun foo(Xs: i32 list) : i32 list =\n  let fun add1(Xs: i32 list, Y: i32) : i32 list = (Y + 1) :: Xs in\n  let fun cons_wrapper(Xs: i32 list, X: i32) : i32 list = X :: Xs in\n  fold[cons_wrapper]([], fold[add1]([], Xs))\n\nrel ok\nok :- foo([1, 2, 3]) = [2, 3, 4].\n"
  },
  {
    "path": "src/test/resources/test287_ok.flg",
    "content": "fun rev(Xs: 'a list) : 'a list =\n  let fun cons_wrapper(Ys: 'a list, X: 'a) : 'a list = X :: Ys in\n  fold[cons_wrapper]([], Xs)\n  \nrel ok\nok :- rev([1, 2, 3]) = [3, 2, 1]."
  },
  {
    "path": "src/test/resources/test288_ok.flg",
    "content": "fun foo(X: i32) : i32 =\n  let fun id(Y: 'a) : 'a = Y in\n  id(X)\n\nrel ok\nok :- foo(42) = 42."
  },
  {
    "path": "src/test/resources/test289_ok.flg",
    "content": "fun foo(X: string, Y: i32) : string =\n  let fun id(Y: 'a) : 'a = Y in\n  string_concat(id(X), to_string(id(Y)))\n\nrel ok\nok :- foo(\"hello #\", 42) = \"hello #42\".\n"
  },
  {
    "path": "src/test/resources/test290_ok.flg",
    "content": "fun addN(Xs: i32 list, N: i32) : i32 list =\n  let fun rev(Xs: 'a list) : 'a list =\n    let fun cons_wrapper(Ys: 'a list, X: 'a) : 'a list = X :: Ys in\n    fold[cons_wrapper]([], Xs) in\n  let fun addN(Xs: i32 list, X: i32) : i32 list = (X + N) :: Xs in\n  rev(fold[addN]([], Xs))\n  \nrel ok\nok :- addN([1, 2, 3], 10) = [11, 12, 13]."
  },
  {
    "path": "src/test/resources/test291_ok.flg",
    "content": "fun foo(Xs: i32 list, M: i32, B: i32) : i32 list =\n  let fun rev(Xs: 'a list) : 'a list =\n    let fun cons_wrapper(Ys: 'a list, X: 'a) : 'a list = X :: Ys in\n    fold[cons_wrapper]([], Xs) in\n  let fun bar(Xs: i32 list, X: i32) : i32 list = (M * X + B) :: Xs in\n  rev(fold[bar]([], Xs))\n  \nrel ok\nok :- foo([1, 2, 3], 2, 1) = [3, 5, 7]."
  },
  {
    "path": "src/test/resources/test292_ok.flg",
    "content": "fun foo(Xs: i32 list, M: i32, B: i32) : i32 list =\n  let fun rev(Xs: 'a list) : 'a list =\n    let fun cons_wrapper(Ys: 'a list, X: 'a) : 'a list = X :: Ys in\n    fold[cons_wrapper]([], Xs) in\n  let\n    fun bar(Xs: i32 list, X: i32) : i32 list = baz(M * X) :: Xs\n    and baz(X: i32) : i32 = X + B in\n  rev(fold[bar]([], Xs))\n  \nrel ok\nok :- foo([1, 2, 3], 2, 1) = [3, 5, 7].\n"
  },
  {
    "path": "src/test/resources/test293_ok.flg",
    "content": "fun foo : i32 * i32 list = (12, [24])"
  },
  {
    "path": "src/test/resources/test294_ok.flg",
    "content": "fun foo : (i32 * i32) list = [(12, 24)]"
  },
  {
    "path": "src/test/resources/test295_bd.flg",
    "content": "fun foo(X: 'a) : 'a smt = `X`\n\nrel not_ok\n\nnot_ok :-\n  some(M) = get_model([`true`], none),\n  foo(M) = _. \n"
  },
  {
    "path": "src/test/resources/test296_bd.flg",
    "content": "type foo = bar(i32)\n\n@edb rel baz(foo smt)\n\nbaz(`bar(#x[i32])`).\n\nrel not_ok\n\nnot_ok :-\n  baz(`bar(X)`),\n  X + 1 = 42."
  },
  {
    "path": "src/test/resources/test297_ok.flg",
    "content": "type foo = bar(i32)\n\n@edb rel baz(foo smt)\n\nbaz(`bar(#x[i32])`).\nbaz(`bar(bv_const(41))`).\n\n@edb rel buz(i32)\n\nbuz(41).\nbuz(42).\n\nrel ok\n\nok :-\n  buz(X),\n  baz(`bar(bv_const(X))`),\n  X + 1 = 42."
  },
  {
    "path": "src/test/resources/test298_ok.flg",
    "content": "rel ok\n\nok :-\n  string_concat(\"\", \"hello\") = \"hello\",\n  string_concat(\"hel\", \"lo\") = \"hello\",\n  string_concat(\"\", \"hello\") = \"hello\"."
  },
  {
    "path": "src/test/resources/test299_ok.flg",
    "content": "rel ok\n\nok :-\n  string_cmp(\"a\", \"bc\") = cmp_lt,\n  string_cmp(\"abc\", \"abc\") = cmp_eq,\n  string_cmp(\"bc\", \"a\") = cmp_gt."
  },
  {
    "path": "src/test/resources/test300_ok.flg",
    "content": "rel ok\n\nok :-\n  to_string(0) = \"0\",\n  to_string(\"0\") = \"0\",\n  to_string(some(0)) = \"some(0)\"."
  },
  {
    "path": "src/test/resources/test301_ok.flg",
    "content": "rel ok\n\nok :-\n  X = #x[bool],\n  Y = #y[bool],\n  is_sat(`some(X) #= some(Y)`),\n  is_sat(`~(some(X) #= some(Y))`)."
  },
  {
    "path": "src/test/resources/test302_bd.flg",
    "content": "rel not_ok\n\nnot_ok :-\n  X = #x[4],\n  Y = #x[4],\n  X = Y."
  },
  {
    "path": "src/test/resources/test303_ok.flg",
    "content": "rel ok\n\nok :-\n  is_valid(`#x[?] ==> #{\"x\"}[?]`)."
  },
  {
    "path": "src/test/resources/test304_ok.flg",
    "content": "rel ok\n\nok :-\n  X = `#x[?] #= 42`,\n  Y = `#y[?] #= 21`,\n  Z = `#x[bv[32]] #= #y[?]`,\n  is_sat_opt([X], none) = some(true),\n  is_sat_opt([Y], none) = some(true),\n  is_sat_opt([Z], none) = some(true),\n  is_sat_opt([X, Y], none) = some(true),\n  is_sat_opt([X, Z], none) = some(true),\n  is_sat_opt([Y, Z], none) = some(true),\n  is_sat_opt([X, Y, Z], none) = some(false).\n  "
  },
  {
    "path": "src/test/resources/test305_ok.flg",
    "content": "rel ok\nok :-\n  is_sat_opt([`true`], none) = some(true),\n  is_sat_opt([`#x[?] #= true`, `true`], none) = some(true),\n  is_sat_opt([`#x[bool] #= #y[?]`, `#x[?] #= true`, `true`], none) = some(true),\n  is_sat_opt([`#y[?] #= false`, `#x[bool] #= #y[?]`, `#x[?] #= true`, `true`], none) = some(false),\n  is_sat_opt([`#y[?] /\\ #x[?]`, `#x[bool] #= #y[?]`, `#x[?] #= true`, `true`], none) = some(true),\n  is_sat_opt([`#x[?] /\\ #x[?]`, `#x[?] #= true`, `true`], none) = some(true),\n  is_sat_opt([`false`], none) = some(false)."
  },
  {
    "path": "src/test/resources/test306_ok.flg",
    "content": "fun f(X: i32, Y: i32) : i32 =\n  let fun g(Z: i32) : i32 =\n    let fun h(W: i32) : i32 = W + Z in\n    h(Z) in\n  g(X + Y)\n\nrel ok\nok :- f(2, 3) = 10."
  },
  {
    "path": "src/test/resources/test307_ok.flg",
    "content": "@edb rel edge(i32, i32, bool smt)\n\nedge(3,\t0,\t`array_select[bv[4]](array_store(#C[(bv[4], bool) array], #B[bv[4]], #E[bool]), bv_big_const[4](-39L))`).\nedge(3,\t9,\t`smt_eq[(bv[4], bv[4]) array](array_store(array_store(#A[(bv[4], bv[4]) array], bv_const[4](6), array_select[bv[4]](array_store(#A[(bv[4], bv[4]) array], bv_const[4](-49), #C[bv[4]]), #A[bv[4]])), #D[bv[4]], array_select[bv[8]](array_store(#E[(bv[8], bv[4]) array], #D[bv[8]], #A[bv[4]]), bv_or(bv_and(bv_big_const[8](-10L), #A[bv[8]]), bv_big_const[8](45L)))), array_store(array_store(array_store(array_store(#D[(bv[4], bv[4]) array], #D[bv[4]], #B[bv[4]]), bv_big_const[4](-19L), bv_to_bv_signed[8,4](#D[bv[8]])), bv_mul(bv_big_const[4](-38L), bv_srem(#D[bv[4]], #A[bv[4]])), bv_add(bv_big_const[4](6L), bv_xor(bv_const[4](24), #E[bv[4]]))), #A[bv[4]], bv_add(#B[bv[4]], bv_const[4](-9))))`).\n\nfun z : (bv[4], bv[8]) array smt =\n  `array_store(\n    #E[(bv[4], bv[8]) array],\n    array_select[bv[8]](#D[(bv[8], bv[4]) array], bv_const[8](1)),\n    array_select[bv[8]](#C[(bv[8], bv[8]) array], bv_const[8](2)))`"
  },
  {
    "path": "src/test/resources/test308_ok.flg",
    "content": "rel ok\n\n(*\n  Check that negative integers are serialized correctly to SMT-LIB\n  (especially for less forgiving solvers like CVC4).\n*)\nok :- is_sat(`#x[int] #= int_const(-1)`)."
  },
  {
    "path": "src/test/resources/test309_ok.flg",
    "content": "fun foo(Xs: i32 list) : i32 =\n  match Xs with\n  | [1, 2] => 0\n  | [X, 3] => X\n  | [1, 3] => 2\n  end\n\nrel ok\n\nok :- foo([1, 3]) = 1."
  },
  {
    "path": "src/test/resources/test310_ok.flg",
    "content": "fun foo(Xs: i32 list) : i32 =\n  match Xs with\n  | [1, 2] => 0\n  | [2, 3] => 1\n  | [1, 3] => 2\n  end\n\nrel ok\n\nok :- foo([1, 3]) = 2."
  },
  {
    "path": "src/test/resources/test311_ok.flg",
    "content": "rel ok1\nrel ok2\nrel ok3\nrel ok4\nrel ok5\nrel ok\n\nok1 :-\n  string_to_list(\"\") = [],\n  string_to_list(\"a\") = [97],\n  string_to_list(\"abc\") = [97, 98, 99].\n\nok2 :-\n  list_to_string([]) = \"\",\n  list_to_string([97]) = \"a\",\n  list_to_string([97, 98, 99]) = \"abc\".\n\nok3 :-\n  char_at(\"\", 0) = none,\n  char_at(\"a\", 0) = some(97),\n  char_at(\"a\", 1) = none,\n  char_at(\"abc\", 0) = some(97),\n  char_at(\"abc\", 1) = some(98),\n  char_at(\"abc\", 2) = some(99),\n  char_at(\"abc\", 3) = none,\n  char_at(\"abc\", 42) = none,\n  char_at(\"abc\", -42) = none.\n\nok4 :-\n  substring(\"\", 0, 0) = some(\"\"),\n  substring(\"\", 0, 1) = none,\n  substring(\"\", -1, 1) = none,\n  substring(\"abc\", 0, 0) = some(\"\"),\n  substring(\"abc\", 0, 1) = some(\"a\"),\n  substring(\"abc\", 0, 2) = some(\"ab\"),\n  substring(\"abc\", 0, 3) = some(\"abc\"),\n  substring(\"abc\", 0, 4) = none.\n\nok5 :-\n  string_length(\"\") = 0,\n  string_length(\"a\") = 1,\n  string_length(\"abc\") = 3.\n\nok :-\n  ok1,\n  ok2,\n  ok3,\n  ok4,\n  ok5.\n"
  },
  {
    "path": "src/test/resources/test312_bd.flg",
    "content": "rel not_ok\n\nnot_ok :- `bv_extract[32,16](42, #x[i32], 15)` = _."
  },
  {
    "path": "src/test/resources/test313_bd.flg",
    "content": "rel not_ok\n\nnot_ok :- `bv_extract[32,16](42, 0, #x[i32])` = _."
  },
  {
    "path": "src/test/resources/test314_bd.flg",
    "content": "rel not_ok\n\nnot_ok :- `int_const(#x[i32])` = _."
  },
  {
    "path": "src/test/resources/test315_bd.flg",
    "content": "rel not_ok\n\nnot_ok :- `int_big_const(#x[i64])` = _."
  },
  {
    "path": "src/test/resources/test316_ok.flg",
    "content": "rel ok1\nrel ok2\nrel ok3\nrel ok4\nrel ok5\nrel ok\n\nok1 :-\n  X = 42,\n  match `X` with\n  | bv_const(42) => true\n  | _ => false\n  end.\t\n\nok2 :-\n  X = 42L,\n  match `X` with\n  | bv_big_const(42L) => true\n  | _ => false\n  end.\n\nok3 :-\n  X = 42F,\n  match `X` with\n  | fp_const(42F) => true\n  | _ => false\n  end.\n  \nok4 :-\n  X = 42D,\n  match `X` with\n  | fp_big_const(42D) => true\n  | _ => false\n  end.\n  \nok5 :-\n  X = \"hello\",\n  match `X` with\n  | `\"hello\"` => true\n  | _ => false\n  end.\n\nok :- ok1, ok2, ok3, ok4, ok5."
  },
  {
    "path": "src/test/resources/test317_ok.flg",
    "content": "rel ok1\nrel ok2\nrel ok3\nrel ok4\nrel ok5\nrel ok\n\nok1 :-\n  X = 42,\n  match bv_const(X) with\n  | `42` => true\n  | _ => false\n  end.\t\n\nok2 :-\n  X = 42L,\n  match bv_big_const(X) with\n  | `42L` => true\n  | _ => false\n  end.\n\nok3 :-\n  X = 42F,\n  match fp_const(X) with\n  | `42F` => true\n  | _ => false\n  end.\n  \nok4 :-\n  X = 42D,\n  match fp_big_const(X) with\n  | `42D` => true\n  | _ => false\n  end.\n  \nok5 :-\n  X = \"hello\",\n  match `X` with\n  | `\"hello\"` => true\n  | _ => false\n  end.\n\nok :- ok1, ok2, ok3, ok4, ok5."
  },
  {
    "path": "src/test/resources/test318_ok.flg",
    "content": "rel ok\n\n@edb rel foo(i32 smt)\n\nfoo(`42`).\n\nok :-\n  foo(`42`),\n  foo(`bv_const(42)`),\n  foo(X),\n  X = `42`,\n  X = `bv_const(42)`,\n  X = `bv_const(Y)`,\n  Y = 42.\n  "
  },
  {
    "path": "src/test/resources/test319_ok.flg",
    "content": "rel ok1\nrel ok2\nrel ok3\nrel ok4\nrel ok5\nrel ok\n\nok1 :-\n  match (let x = 42 in `x`) with\n  | `bv_const(42)` => true\n  | _ => false\n  end.\n  \nok2 :-\n  match (let x = 42L in `x`) with\n  | `bv_big_const(42L)` => true\n  | _ => false\n  end.\n  \nok3 :-\n  match (let x = 42F in `x`) with\n  | `fp_const(42F)` => true\n  | _ => false\n  end.\n  \nok4 :-\n  match (let x = 42D in `x`) with\n  | `fp_big_const(42D)` => true\n  | _ => false\n  end.\n  \nok5 :-\n  match (let x = \"hello\" in `x`) with\n  | `\"hello\"` => true\n  | _ => false\n  end.\n \n ok :- ok1, ok2, ok3, ok4, ok5. "
  },
  {
    "path": "src/test/resources/test320_ok.flg",
    "content": "(* This recreates a bug having to do with let fun expressions inside match\n   expressions. *)\n\nrel ok\n\nfun foo(x: 'a) : 'a =\n  match true with\n  | true =>\n  \tlet fun id(x: 'a) : 'a = x in\n  \tid(x)\n  end\n\nok :- foo(42) = 42. "
  },
  {
    "path": "src/test/resources/test321_ok.flg",
    "content": "(* This makes sure that variable usage checks work correctly in the presence of\n   rules with multiple head predicates. *)\n\n@edb rel foobar(i32, string)\n\nfoobar(42, \"hello\").\n\nrel foo(i32)\n\nrel bar(string)\n\nfoo(X), bar(Y) :- foobar(X, Y).\n\nrel ok\n\nok :- foo(42), bar(\"hello\")."
  },
  {
    "path": "src/test/resources/test322_bd.flg",
    "content": "(* This makes sure that variable usage checks work correctly in the presence of\n   rules with multiple head predicates. *)\n\n@edb rel foobarbaz(i32, string, i32)\n\nrel foo(i32)\n\nrel bar(string)\n\nfoo(X), bar(Y) :- foobarbaz(X, Y, Z)."
  },
  {
    "path": "src/test/resources/test323_ok.flg",
    "content": "@edb rel foo(arg1: i32, arg2: i32, foo: i32)\n\nrel bar(foo: i32, list: i32 list)\n\nfoo(1, 2, 3).\n\nbar(1, []).\n\nrel ok\n\nok :- foo(1, 2, 3), bar(1, [])."
  },
  {
    "path": "src/test/resources/test324_ok.flg",
    "content": "rel ok\n\nok :-\n  string_to_i32(\"42\") = some(42),\n  string_to_i32(\"+42\") = some(42),\n  string_to_i32(\"-42\") = some(-42),\n  string_to_i32(\"0x2a\") = some(42),\n  string_to_i32(\"0xffffffff\") = some(-1),\n  \n  string_to_i32(\"42f\") = none,\n  string_to_i32(\"42d\") = none,\n  string_to_i32(\"42l\") = none,\n  string_to_i32(\"42F\") = none,\n  string_to_i32(\"42D\") = none,\n  string_to_i32(\"42L\") = none,\n  string_to_i32(\"42.\") = none,\n  string_to_i32(\"0x100000000\") = none."
  },
  {
    "path": "src/test/resources/test325_ok.flg",
    "content": "rel ok\n\nok :-\n  string_to_i64(\"42\") = some(42L),\n  string_to_i64(\"+42\") = some(42L),\n  string_to_i64(\"-42\") = some(-42L),\n  string_to_i64(\"0x2a\") = some(42L),\n  string_to_i64(\"0xffffffffffffffff\") = some(-1L),\n  \n  string_to_i64(\"42f\") = none,\n  string_to_i64(\"42d\") = none,\n  string_to_i64(\"42l\") = none,\n  string_to_i64(\"42F\") = none,\n  string_to_i64(\"42D\") = none,\n  string_to_i64(\"42L\") = none,\n  string_to_i64(\"42.\") = none,\n  string_to_i64(\"0x10000000000000000\") = none."
  },
  {
    "path": "src/test/resources/test326_ok.flg",
    "content": "rel empty_ok\nrel plus_ok\nrel minus_ok\nrel union_ok\nrel diff_ok\nrel singleton_ok\nrel choose_ok\nrel size_ok\nrel subset_ok\nrel ok\n\nfun opaque_set_id(s: 'a opaque_set) : 'a opaque_set = s\n\nfun from_list(xs: 'a list) : 'a opaque_set =\n  match xs with\n  | [] => opaque_set_empty\n  | h :: t => opaque_set_plus(h, from_list(t))\n  end\n\nempty_ok :-\n  opaque_set_empty = opaque_set_id(opaque_set_empty).  \n\nplus_ok :-\n  from_list([1, 2, 3]) = from_list([3, 1, 2]),\n  from_list([1, 2, 3]) != from_list([1, 2]).\n\nminus_ok :-\n  opaque_set_minus(2, from_list([1, 2, 3])) = from_list([1, 3]).\n\nunion_ok :-\n  opaque_set_union(from_list([1, 2]), from_list([3, 4])) =\n    from_list([1, 2, 3, 4]).\n\ndiff_ok :-\n  opaque_set_diff(from_list([1, 2, 3, 4]), from_list([2, 3])) =\n    from_list([1, 4]).\n\nsingleton_ok :-\n  opaque_set_singleton(42) = from_list([42]).\n\nchoose_ok :-\n  S1 = from_list([1, 2]),\n  some((X, S2)) = opaque_set_choose(S1),\n  some((Y, S3)) = opaque_set_choose(S2),\n  none = opaque_set_choose(S3),\n  X != Y,\n  X = 1 || Y = 1,\n  X = 2 || Y = 2.\n\nsize_ok :-\n  opaque_set_size(opaque_set_empty) = 0,\n  opaque_set_size(opaque_set_singleton(\"hello\")) = 1,\n  opaque_set_size(from_list([1, 2, 3, 4])) = 4.\n\nsubset_ok :-\n  opaque_set_subset(from_list([2, 4]), from_list([1, 2, 3, 4])),\n  !opaque_set_subset(from_list([2, 4]), from_list([1, 2, 3, 5])).\n\nok :-\n  empty_ok,\n  plus_ok,\n  minus_ok,\n  union_ok,\n  diff_ok,\n  singleton_ok,\n  choose_ok,\n  size_ok,\n  subset_ok,\n  true.\n"
  },
  {
    "path": "src/test/resources/test327_bd.flg",
    "content": "rel not_ok\n\nnot_ok :-\n  S = opaque_set_singleton(42),\n  is_sat(`S #= S`).\n"
  },
  {
    "path": "src/test/resources/test328_ok.flg",
    "content": "rel ok\n\nok :-\n\tF = [`#x[?] #= 42`, `#y[?] #= 13`],\n\tis_set_sat(opaque_set_from_list(F), none) = some(true),\n\tis_set_sat(opaque_set_from_list(`#x[i32] #= #y[?]` :: F), none) = some(false)."
  },
  {
    "path": "src/test/resources/test329_ok.flg",
    "content": "rel ok\n\nok :-\n  is_sat(`bv_to_int(42) #= int_const(42)`),\n  is_sat(`int_to_bv(int_const(42)) #= 42`),\n  some(M) =\n    get_model([\n      `int_gt(#x[int], int_const(0))`,\n      `int_lt(#x[int], int_const(2))`,\n      `int_to_bv(#x[int]) #= #y[i32]`], none),\n  query_model(#y[i32], M) = some(1)."
  },
  {
    "path": "src/test/resources/test330_ok.flg",
    "content": "rel ok\n\nok :-\n  X = `bv_extract[32, 16](#x[?], 0, 15)`,\n  Y = `bv_extract[32, 16](#x[?], 16, 31)`,\n  is_valid(`bv_concat(Y, X) #= #x[i32]`)."
  },
  {
    "path": "src/test/resources/test331_ok.flg",
    "content": "rel foo(i32)\nrel bar(i32 option)\nrel ok\n\nbar(none).\n\nfoo(X + 1) :- bar(some(X)).\n\nbar(some(42)) :- foo(42).\n\nok :- !foo(42)."
  },
  {
    "path": "src/test/resources/test332_ok.flg",
    "content": "@edb rel entity(string)\nentity(\"Alice\").\nentity(\"Bob\").\nentity(\"World\").\n\nrel greeting(string)\ngreeting(Y) :-\n  entity(X),\n  some(M) = get_model([`#y[string] #= str_concat(\"Hello, \", X)`], none),\n  some(Y) = query_model(#y[string], M).\n\nrel ok\nok :-\n  greeting(\"Hello, World\"),\n  greeting(\"Hello, Alice\"),\n  greeting(\"Hello, Bob\")."
  },
  {
    "path": "src/test/resources/test333_bd.flg",
    "content": "fun hd(xs: 'a list) : 'a = let x :: _ = xs in x\n\nfun foo(xs: 'a list) : bool smt = let x = hd(xs) in `x`"
  },
  {
    "path": "src/test/resources/test334_ok.flg",
    "content": "const x: i32 = 42\n\nconst y: i32 = 84\nand z: string = \"hello\"\n\nrel ok\nok :-\n  x = 42,\n  y = 84,\n  z = \"hello\"."
  },
  {
    "path": "src/test/resources/test335_bd.flg",
    "content": "const bad(x: i32, y: i32, z: i32): i32 = x + y + z"
  },
  {
    "path": "src/test/resources/test336_ok.flg",
    "content": "rel ok\n\nok :-\n\ti32_udiv(-2, 2) = 0x7fffffff,\n\ti32_urem(-4, -3) = -4,\n\ti32_shl(1, 2) = 4,\n\ti32_ashr(-1, 1) = 0xffffffff,\n\ti32_lshr(-1, 1) = 0x7fffffff."
  },
  {
    "path": "src/test/resources/test337_ok.flg",
    "content": "rel ok\n\nok :-\n\ti64_udiv(-2L, 2L) = 0x7fffffffffffffffL,\n\ti64_urem(-4L, -3L) = -4L,\n\ti64_shl(1L, 2L) = 4L,\n\ti64_ashr(-1L, 1L) = 0xffffffffffffffffL,\n\ti64_lshr(-1L, 1L) = 0x7fffffffffffffffL."
  },
  {
    "path": "src/test/resources/test338_ok.flg",
    "content": "(* Test case for issue #72 <https://github.com/HarvardPL/formulog/issues/72> *)\n\nrel foo(bool smt)\n\nfoo(`true`).\n\nrel not_ok\n\nnot_ok :- foo(`#x[i32] #= _`).\n\nrel ok\n\nok :- !not_ok.\n"
  },
  {
    "path": "src/test/resources/test339_ok.flg",
    "content": "(* Test case for issue #74 <https://github.com/HarvardPL/formulog/issues/74> *)\n\nrel bar(i32)\n\nrel foo(i32 smt)\n\nrel baz\n\nfoo(`#x[?]`).\n\nbaz :-\n  bar(X),\n  foo(`X`),\n  X + 1 = 42.\n\nbar(0).\n\n(* Make sure `foo` is recursive in `ok` stratum, so that when `ok` rule is run,\n   `foo` predicate is reordered to be first *) \nfoo(`0`) :- baz.\n\nfoo(`#x0[?]`).\nfoo(`#x1[?]`).\nfoo(`#x2[?]`).\nfoo(`#x3[?]`).\nfoo(`#x4[?]`).\nfoo(`#x5[?]`).\nfoo(`#x6[?]`).\nfoo(`#x7[?]`).\nfoo(`#x8[?]`).\nfoo(`#x9[?]`).\nfoo(`#xa[?]`).\nfoo(`#xb[?]`).\nfoo(`#xc[?]`).\nfoo(`#xd[?]`).\nfoo(`#xe[?]`).\nfoo(`#xf[?]`).\n\nfoo(`41`).\nbar(41).\n\nfun len(xs: 'a list): i32 =\n  match xs with\n  | [] => 0\n  | _ :: t => 1 + len(t)\n  end\n\nrel ok\n\nok :-\n  baz,\n  len(foo(??)) = 19.\n"
  },
  {
    "path": "src/test/resources/test340_ok.flg",
    "content": "(* Test case for issue #80 <https://github.com/HarvardPL/formulog/issues/80> *)\n\nrel a(i32)\nrel b(i32)\nrel ok\n\na(1-1).\nb(2+2).\n\nok :- a(0), b(4).\n"
  },
  {
    "path": "src/test/resources/test341_bd.flg",
    "content": "rel ok\n\nok :- `-0x1 #= -1` = _.\n"
  },
  {
    "path": "src/test/resources/test342_bd.flg",
    "content": "rel ok\n\nok :- `-0x1L #= -1L` = _.\n"
  },
  {
    "path": "src/test/resources/test343_bd.flg",
    "content": "rel ok\n\nok :- `+0x1 #= 1` = _.\n"
  },
  {
    "path": "src/test/resources/test344_bd.flg",
    "content": "rel ok\n\nok :- `+0x1L #= 1L` = _.\n"
  },
  {
    "path": "src/test/resources/test345_ok.flg",
    "content": "rel ok\n\nok :-\n    `-1 #= +1` = _,\n    `-1L #= +1l` = _,\n    `-1F #= +1f` = _,\n    `-1D #= +1d` = _.\n\n"
  }
]