[
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "content": "name: Bug report\ndescription: Create a report to help us improve.\nbody:\n - type: markdown\n   attributes: \n     value: |\n      ### Bug Report\n      Note, your issue might have been already reported, please check [issues](https://github.com/HigherOrderCO/HVM/issues). If you find a similar issue, respond with a reaction or any additional information that you feel may be helpful.\n\n      ### For Windows Users\n      There is currently no native way to install HVM, as a temporary workaround, please use [WSL2](https://learn.microsoft.com/en-us/windows/wsl/install).\n  \n - type: textarea\n   attributes:\n    label: Reproducing the behavior\n    description: A clear and concise description of what the bug is.\n    value: |\n      Example:\n       Running command...\n       With code....\n       Error...\n       Expected behavior....\n   validations:\n    required: true\n \n - type: textarea\n   attributes:\n    label: System Settings\n    description: Your System's settings\n    value: |\n     Example:\n      - OS: [e.g. Linux (Ubuntu 22.04)]\n      - CPU: [e.g. Intel i9-14900KF]\n      - GPU: [e.g. RTX 4090]\n      - Cuda Version [e.g. release 12.4, V12.4.131]\n   validations:\n     required: true\n - type: textarea\n   attributes:\n    label: Additional context\n    description: Add any other context about the problem here (Optional). \n   \n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links: \n    - name: Bend Related Issues\n      url: https://github.com/HigherOrderCO/Bend/issues/new/choose\n      about: For Bend related Issues, please Report them on the Bend repository.  \n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest a feature that you think should be added.\ntitle: ''\nlabels: ''\n\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/workflows/bench.yml",
    "content": "name: Bench\n\non:\n  pull_request:\n\nconcurrency:\n  group: bench-${{ github.ref }}\n  cancel-in-progress: true\n\njobs:\n  bench:\n    runs-on: [self-hosted, cuda]\n    timeout-minutes: 10\n    steps:\n      - uses: actions/checkout@v3\n      - name: compare perf\n        run: |\n          git fetch origin main\n          git clone https://github.com/HigherOrderCO/hvm-bench\n          cd hvm-bench\n          NO_COLOR=1 cargo run bench --repo-dir ../ -r main --timeout 20 > ../table\n        shell: bash -l {0}\n      - name: write comment\n        run: |\n          echo 'Perf run for [`'`git rev-parse --short ${{ github.sha }}`'`](https://github.com/higherorderco/HVM/commit/${{ github.sha }}):' >> comment\n          echo '```' >> comment\n          cat table >> comment\n          echo '```' >> comment\n      - name: post comment\n        run: gh pr comment ${{ github.event.number }} -F comment\n        env:\n          GH_TOKEN: ${{ secrets.PAT }}\n      - name: hide old comment\n        env:\n          GH_TOKEN: ${{ secrets.PAT }}\n        run: |\n          COMMENT_ID=$(\n            gh api graphql -F pr=${{ github.event.number }} -f query='\n              query($pr: Int!) {\n                organization(login: \"higherorderco\") {\n                  repository(name: \"HVM\") {\n                    pullRequest(number: $pr) {\n                      comments(last: 100) {\n                        nodes { id author { login } }\n                      }\n                    }\n                  }\n                }\n            }\n            ' \\\n            | jq -r '\n              [\n                .data.organization.repository.pullRequest.comments.nodes | .[]\n                | select(.author.login == \"HigherOrderBot\")\n                | .id\n              ] | .[-2]\n            '\n          )\n\n          if [ $COMMENT_ID != null ]\n          then\n            gh api graphql -F id=$COMMENT_ID -f query='\n              mutation($id: ID!) {\n                minimizeComment(input: {\n                  subjectId: $id,\n                  classifier: OUTDATED,\n                }) { minimizedComment { ...on Comment { id } } }\n              }\n            '\n          fi\n      - name: delete on cancel\n        if: ${{ cancelled() }}\n        run: gh workflow run delete-cancelled.yml -f run_id=${{ github.run_id }}\n        env:\n          GH_TOKEN: ${{ secrets.PAT }}\n"
  },
  {
    "path": ".github/workflows/checks.yml",
    "content": "name: Checks\n\non:\n  pull_request:\n  merge_group:\n  push:\n    branches:\n      - main\n\njobs:\n  check:\n    runs-on: ubuntu-latest\n    timeout-minutes: 10\n    steps:\n      - uses: actions/checkout@v3\n      - uses: actions/cache@v2\n        with:\n          path: |\n            ~/.cargo/registry\n            ~/.cargo/git\n            target\n          key: ${{ runner.os }}-check-${{ hashFiles('**/Cargo.lock') }}\n      - run: RUSTFLAGS=\"-D warnings\" cargo check --all-targets\n  test:\n    runs-on: ubuntu-latest\n    timeout-minutes: 10\n    steps:\n      - uses: actions/checkout@v3\n      - uses: actions/cache@v2\n        with:\n          path: |\n            ~/.cargo/registry\n            ~/.cargo/git\n            target\n          key: ${{ runner.os }}-test-${{ hashFiles('**/Cargo.lock') }}\n      - run: cargo test --release\n  test-cuda:\n    needs: test # don't bother the cuda machine if other tests are failing\n    runs-on: [self-hosted, cuda]\n    timeout-minutes: 10\n    steps:\n      - uses: actions/checkout@v3\n      - run: cargo test --release\n        shell: bash -l {0}\n\n"
  },
  {
    "path": ".github/workflows/delete-cancelled.yml",
    "content": "name: Delete Cancelled Benchmarks\n\non:\n  workflow_dispatch:\n    inputs:\n      run_id:\n        type: string\n        description: \"\" \n\njobs:\n  delete:\n    runs-on: ubuntu-latest\n    steps:\n      - run: gh api \"repos/higherorderco/hvm-core/actions/runs/${{ inputs.run_id }}\" -X DELETE\n        env:\n          GH_TOKEN: ${{ secrets.PAT }}\n"
  },
  {
    "path": ".gitignore",
    "content": ".fill.tmp\nhvm-cuda-experiments/\nsrc/hvm\nsrc/old_cmp.rs\nsrc/tmp/\ntarget/\ntmp/\n.hvm/\nexamples/**/main\nexamples/**/*.c\nexamples/**/*.cu\n.out.hvm\n\n# nix-direnv\n/.direnv/\n/.envrc\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[package]\nname = \"hvm\"\ndescription = \"A massively parallel, optimal functional runtime in Rust.\"\nlicense = \"Apache-2.0\"\nversion = \"2.0.22\"\nedition = \"2021\"\nrust-version = \"1.74\"\nbuild = \"build.rs\"\nrepository = \"https://github.com/HigherOrderCO/HVM\"\n\n[lib]\nname = \"hvm\"\npath = \"src/lib.rs\"\n\n[dependencies]\nTSPL = \"0.0.13\"\nclap = \"4.5.2\"\nhighlight_error = \"0.1.1\"\nnum_cpus = \"1.0\"\n\n[build-dependencies]\ncc = \"1.0\"\nnum_cpus = \"1.0\"\n\n[features]\ndefault = []\n# C and CUDA features are determined during build\nc = []\ncuda = []\n\n[dev-dependencies]\ninsta = { version = \"1.39.0\", features = [\"glob\"] }\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n"
  },
  {
    "path": "README.md",
    "content": "Higher-order Virtual Machine 2 (HVM2)\n=====================================\n\n**Higher-order Virtual Machine 2 (HVM2)** is a massively parallel [Interaction\nCombinator](https://www.semanticscholar.org/paper/Interaction-Combinators-Lafont/6cfe09aa6e5da6ce98077b7a048cb1badd78cc76)\nevaluator.\n\nBy compiling programs from high-level languages (such as Python and Haskell) to\nHVM, one can run these languages directly on massively parallel hardware, like\nGPUs, with near-ideal speedup.\n\nHVM2 is the successor to [HVM1](https://github.com/HigherOrderCO/HVM1), a 2022\nprototype of this concept. Compared to its predecessor, HVM2 is simpler, faster\nand, most importantly, more correct. [HOC](https://HigherOrderCO.com/) provides\nlong-term support for all features listed on its [PAPER](./paper/HVM2.pdf).\n\nThis repository provides a low-level IR language for specifying the HVM2 nets\nand a compiler from that language to C and CUDA. It is not meant for direct\nhuman usage. If you're looking for a high-level language to interface with HVM2,\ncheck [Bend](https://github.com/HigherOrderCO/Bend) instead.\n\nUsage\n-----\n\n> DISCLAIMER: Windows is currently not supported, please use [WSL](https://learn.microsoft.com/en-us/windows/wsl/install) for now as a workaround.\n\nFirst install the dependencies:\n* If you want to use the C runtime, install a C-11 compatible compiler like GCC or Clang.\n* If you want to use the CUDA runtime, install CUDA and nvcc (the CUDA compiler).\n  - _HVM requires CUDA 12.x and currently only works on Nvidia GPUs._\n\nInstall HVM2:\n\n```sh\ncargo install hvm\n```\n\nThere are multiple ways to run an HVM program:\n\n```sh\nhvm run    <file.hvm> # interpret via Rust\nhvm run-c  <file.hvm> # interpret via C\nhvm run-cu <file.hvm> # interpret via CUDA\nhvm gen-c  <file.hvm> # compile to standalone C\nhvm gen-cu <file.hvm> # compile to standalone CUDA\n```\n\nAll modes produce the same output. The compiled modes require you to compile the\ngenerated file (with `gcc file.c -o file`, for example), but are faster to run.\nThe CUDA versions have much higher peak performance but are less stable. As a\nrule of thumb, `gen-c` should be used in production.\n\nLanguage\n--------\n\nHVM is a low-level compile target for high-level languages. It provides a raw\nsyntax for wiring interaction nets. For example:\n\n```javascript\n@main = a\n  & @sum ~ (28 (0 a))\n\n@sum = (?(((a a) @sum__C0) b) b)\n\n@sum__C0 = ({c a} ({$([*2] $([+1] d)) $([*2] $([+0] b))} f))\n  &! @sum ~ (a (b $([+] $(e f))))\n  &! @sum ~ (c (d e))\n```\n\nThe file above implements a recursive sum. If that looks unreadable to you -\ndon't worry, it isn't meant to. [Bend](https://github.com/HigherOrderCO/Bend) is\nthe human-readable language and should be used both by end users and by languages\naiming to target the HVM. If you're looking to learn more about the core\nsyntax and tech, though, please check the [PAPER](./paper/HVM2.pdf).\n"
  },
  {
    "path": "build.rs",
    "content": "fn main() {\n  let cores = num_cpus::get();\n  let tpcl2 = (cores as f64).log2().floor() as u32;\n\n  println!(\"cargo:rerun-if-changed=src/run.c\");\n  println!(\"cargo:rerun-if-changed=src/hvm.c\");\n  println!(\"cargo:rerun-if-changed=src/run.cu\");\n  println!(\"cargo:rerun-if-changed=src/hvm.cu\");\n  println!(\"cargo:rustc-link-arg=-rdynamic\");\n\n  match cc::Build::new()\n      .file(\"src/run.c\")\n      .opt_level(3)\n      .warnings(false)\n      .define(\"TPC_L2\", &*tpcl2.to_string())\n      .define(\"IO\", None)\n      .try_compile(\"hvm-c\") {\n    Ok(_) => println!(\"cargo:rustc-cfg=feature=\\\"c\\\"\"),\n    Err(e) => {\n      println!(\"cargo:warning=\\x1b[1m\\x1b[31mWARNING: Failed to compile/run.c:\\x1b[0m {}\", e);\n      println!(\"cargo:warning=Ignoring/run.c and proceeding with build. \\x1b[1mThe C runtime will not be available.\\x1b[0m\");\n    }\n  }\n\n  // Builds hvm.cu\n  if std::process::Command::new(\"nvcc\").arg(\"--version\").stdout(std::process::Stdio::null()).stderr(std::process::Stdio::null()).status().is_ok() {\n    if let Ok(cuda_path) = std::env::var(\"CUDA_HOME\") {\n      println!(\"cargo:rustc-link-search=native={}/lib64\", cuda_path);\n    } else {\n      println!(\"cargo:rustc-link-search=native=/usr/local/cuda/lib64\");\n    }\n\n    cc::Build::new()\n      .cuda(true)\n      .file(\"src/run.cu\")\n      .define(\"IO\", None)\n      .flag(\"-diag-suppress=177\") // variable was declared but never referenced\n      .flag(\"-diag-suppress=550\") // variable was set but never used\n      .flag(\"-diag-suppress=20039\") // a __host__ function redeclared with __device__, hence treated as a __host__ __device__ function\n      .compile(\"hvm-cu\");\n\n    println!(\"cargo:rustc-cfg=feature=\\\"cuda\\\"\");\n  }\n  else {\n    println!(\"cargo:warning=\\x1b[1m\\x1b[31mWARNING: CUDA compiler not found.\\x1b[0m \\x1b[1mHVM will not be able to run on GPU.\\x1b[0m\");\n  }\n}\n"
  },
  {
    "path": "examples/demo_io/main.bend",
    "content": "test-io = 1\n\ndef unwrap(res):\n  match res:\n    case Result/Ok:\n      return res.val\n    case Result/Err:\n      return res.val\n\ndef open():\n  return call(\"OPEN\", (\"./LICENSE\", \"r\"))\n\ndef read(f):\n  return call(\"READ\", (f, 47))\n\ndef print(bytes):\n  with IO:\n    * <- call(\"WRITE\", (1, bytes))\n    * <- call(\"WRITE\", (1, \"\\n\"))\n\n    return wrap(*)\n\ndef close(f):\n  return call(\"CLOSE\", f)\n\ndef main():\n  with IO:\n    f <- open()\n    f = unwrap(f)\n    bytes <- read(f)\n    bytes = unwrap(bytes)\n    * <- print(bytes)\n    res <- close(f)\n\n    return wrap(res)\n"
  },
  {
    "path": "examples/demo_io/main.hvm",
    "content": "@IO/Call = (a (b (c (d ((@IO/Call/tag (a (b (c (d e))))) e)))))\n\n@IO/Call/tag = 1\n\n@IO/Done = (a (b ((@IO/Done/tag (a (b c))) c)))\n\n@IO/Done/tag = 0\n\n@IO/MAGIC = (13683217 16719857)\n\n@IO/bind = ((@IO/bind__C2 a) a)\n\n@IO/bind__C0 = (* (b (a c)))\n  & @undefer ~ (a (b c))\n\n@IO/bind__C1 = (* (* (a (b ((c d) (e g))))))\n  & @IO/Call ~ (@IO/MAGIC (a (b ((c f) g))))\n  & @IO/bind ~ (d (e f))\n\n@IO/bind__C2 = (?((@IO/bind__C0 @IO/bind__C1) a) a)\n\n@IO/wrap = a\n  & @IO/Done ~ (@IO/MAGIC a)\n\n@String/Cons = (a (b ((@String/Cons/tag (a (b c))) c)))\n\n@String/Cons/tag = 1\n\n@String/Nil = ((@String/Nil/tag a) a)\n\n@String/Nil/tag = 0\n\n@call = (a (b c))\n  & @IO/Call ~ (@IO/MAGIC (a (b (@call__C0 c))))\n\n@call__C0 = a\n  & @IO/Done ~ (@IO/MAGIC a)\n\n@close = f\n  & @call ~ (e f)\n  & @String/Cons ~ (67 (d e))\n  & @String/Cons ~ (76 (c d))\n  & @String/Cons ~ (79 (b c))\n  & @String/Cons ~ (83 (a b))\n  & @String/Cons ~ (69 (@String/Nil a))\n\n@main = w\n  & @IO/bind ~ (@open ((((s (a u)) (@IO/wrap v)) v) w))\n  & @IO/bind ~ (c ((((n (o (d q))) (r (s t))) t) u))\n  & @unwrap ~ (a {b r})\n  & @read ~ (b c)\n  & @IO/bind ~ (f ((((g (k (* m))) (n (o p))) p) q))\n  & @print ~ (e f)\n  & @unwrap ~ (d e)\n  & @IO/bind ~ (h ((((i i) (k l)) l) m))\n  & @close ~ (g h)\n\n@open = o\n  & @call ~ (d ((m n) o))\n  & @String/Cons ~ (79 (c d))\n  & @String/Cons ~ (80 (b c))\n  & @String/Cons ~ (69 (a b))\n  & @String/Cons ~ (78 (@String/Nil a))\n  & @String/Cons ~ (46 (l m))\n  & @String/Cons ~ (47 (k l))\n  & @String/Cons ~ (76 (j k))\n  & @String/Cons ~ (73 (i j))\n  & @String/Cons ~ (67 (h i))\n  & @String/Cons ~ (69 (g h))\n  & @String/Cons ~ (78 (f g))\n  & @String/Cons ~ (83 (e f))\n  & @String/Cons ~ (69 (@String/Nil e))\n  & @String/Cons ~ (114 (@String/Nil n))\n\n@print = (f h)\n  & @IO/bind ~ (g (@print__C3 h))\n  & @call ~ (e ((1 f) g))\n  & @String/Cons ~ (87 (d e))\n  & @String/Cons ~ (82 (c d))\n  & @String/Cons ~ (73 (b c))\n  & @String/Cons ~ (84 (a b))\n  & @String/Cons ~ (69 (@String/Nil a))\n\n@print__C0 = ((* a) (* a))\n\n@print__C1 = g\n  & @call ~ (e ((1 f) g))\n  & @String/Cons ~ (87 (d e))\n  & @String/Cons ~ (82 (c d))\n  & @String/Cons ~ (73 (b c))\n  & @String/Cons ~ (84 (a b))\n  & @String/Cons ~ (69 (@String/Nil a))\n  & @String/Cons ~ (10 (@String/Nil f))\n\n@print__C2 = (a (* c))\n  & @IO/bind ~ (@print__C1 (((@print__C0 (a b)) b) c))\n\n@print__C3 = ((@print__C2 (@IO/wrap a)) a)\n\n@read = (e f)\n  & @call ~ (d ((e 47) f))\n  & @String/Cons ~ (82 (c d))\n  & @String/Cons ~ (69 (b c))\n  & @String/Cons ~ (65 (a b))\n  & @String/Cons ~ (68 (@String/Nil a))\n\n@test-io = 1\n\n@undefer = (((a a) b) b)\n\n@unwrap = ((@unwrap__C0 a) a)\n\n@unwrap__C0 = (?(((a a) (* (b b))) c) c)\n\n\n"
  },
  {
    "path": "examples/sort_bitonic/main.bend",
    "content": "def gen(d, x):\n  switch d:\n    case 0:\n      return x\n    case _:\n      return (gen(d-1, x * 2 + 1), gen(d-1, x * 2))\n\ndef sum(d, t):\n  switch d:\n    case 0:\n      return t\n    case _:\n      (t.a, t.b) = t\n      return sum(d-1, t.a) + sum(d-1, t.b)\n\ndef swap(s, a, b):\n  switch s:\n    case 0:\n      return (a,b)\n    case _:\n      return (b,a)\n\ndef warp(d, s, a, b):\n  switch d:\n    case 0:\n      return swap(s ^ (a > b), a, b)\n    case _:\n      (a.a,a.b) = a\n      (b.a,b.b) = b\n      (A.a,A.b) = warp(d-1, s, a.a, b.a)\n      (B.a,B.b) = warp(d-1, s, a.b, b.b)\n      return ((A.a,B.a),(A.b,B.b))\n\ndef flow(d, s, t):\n  switch d:\n    case 0:\n      return t\n    case _:\n      (t.a, t.b) = t\n      return down(d, s, warp(d-1, s, t.a, t.b))\n\ndef down(d,s,t):\n  switch d:\n    case 0:\n      return t\n    case _:\n      (t.a, t.b) = t\n      return (flow(d-1, s, t.a), flow(d-1, s, t.b))\n\ndef sort(d, s, t):\n  switch d:\n    case 0:\n      return t\n    case _:\n      (t.a, t.b) = t\n      return flow(d, s, (sort(d-1, 0, t.a), sort(d-1, 1, t.b)))\n\ndef main:\n  return sum(12, sort(12, 0, gen(12, 0)))\n"
  },
  {
    "path": "examples/sort_bitonic/main.hvm",
    "content": "@down = (?(((a (* a)) @down__C0) (b (c d))) (c (b d)))\n\n@down__C0 = ({a e} ((c g) ({b f} (d h))))\n  &! @flow ~ (a (b (c d)))\n  &! @flow ~ (e (f (g h)))\n\n@flow = (?(((a (* a)) @flow__C0) (b (c d))) (c (b d)))\n\n@flow__C0 = ({$([+1] a) c} ((e f) ({b d} h)))\n  & @down ~ (a (b (g h)))\n  & @warp ~ (c (d (e (f g))))\n\n@gen = (?(((a a) @gen__C0) b) b)\n\n@gen__C0 = ({a d} ({$([*2] $([+1] b)) $([*2] e)} (c f)))\n  &! @gen ~ (a (b c))\n  &! @gen ~ (d (e f))\n\n@main = a\n  & @sum ~ (12 (@main__C1 a))\n\n@main__C0 = a\n  & @gen ~ (12 (0 a))\n\n@main__C1 = a\n  & @sort ~ (12 (0 (@main__C0 a)))\n\n@sort = (?(((a (* a)) @sort__C0) (b (c d))) (c (b d)))\n\n@sort__C0 = ({$([+1] a) {c f}} ((d g) (b i)))\n  & @flow ~ (a (b ((e h) i)))\n  &! @sort ~ (c (0 (d e)))\n  &! @sort ~ (f (1 (g h)))\n\n@sum = (?(((a a) @sum__C0) b) b)\n\n@sum__C0 = ({a c} ((b d) f))\n  &! @sum ~ (a (b $([+] $(e f))))\n  &! @sum ~ (c (d e))\n\n@swap = (?((@swap__C0 @swap__C1) (a (b c))) (b (a c)))\n\n@swap__C0 = (b (a (a b)))\n\n@swap__C1 = (* (a (b (a b))))\n\n@warp = (?((@warp__C0 @warp__C1) (a (b (c d)))) (c (b (a d))))\n\n@warp__C0 = ({a e} ({$([>] $(a b)) d} ($([^] $(b c)) f)))\n  & @swap ~ (c (d (e f)))\n\n@warp__C1 = ({a f} ((d i) ((c h) ({b g} ((e j) (k l))))))\n  &! @warp ~ (f (g (h (i (j l)))))\n  &! @warp ~ (a (b (c (d (e k)))))\n"
  },
  {
    "path": "examples/sort_radix/main.bend",
    "content": "\n# data Arr = Empty | (Single x) | (Concat x0 x1)\nEmpty  =         λempty λsingle λconcat empty\nSingle = λx      λempty λsingle λconcat (single x)\nConcat = λx0 λx1 λempty λsingle λconcat (concat x0 x1)\n\n# data Map = Free | Busy | (Node x0 x1)\nFree = λfree λbusy λnode free\nBusy = λfree λbusy λnode busy\nNode = λx0 λx1 λfree λbusy λnode (node x0 x1)\n\n# gen : u32 -> Arr\ngen = λn switch n {\n  0: λx (Single x)\n  _: λx\n    let x0 = (* x 2)\n    let x1 = (+ x0 1)\n    (Concat (gen n-1 x1) (gen n-1 x0))\n}\n\n# sum : Arr -> u32\nsum = λa\n  let a_empty  = 0\n  let a_single = λx x\n  let a_concat = λx0 λx1 (+ (sum x0) (sum x1))\n  (a a_empty a_single a_concat)\n\n# sort : Arr -> Arr\nsort = λt (to_arr (to_map t) 0)\n\n# to_arr : Map -> u32 -> Arr\nto_arr = λa\n  let a_free = λk Empty\n  let a_busy = λk (Single k)\n  let a_node = λx0 λx1 λk\n    let x0 = (to_arr x0 (+ (* k 2) 0))\n    let x1 = (to_arr x1 (+ (* k 2) 1))\n    (Concat x0 x1)\n  (a a_free a_busy a_node)\n\n# to_map : Arr -> Map\nto_map = λa\n  let a_empty  = Free\n  let a_single = λx (radix 24 x 1 Busy)\n  let a_concat = λx0 λx1 (merge (to_map x0) (to_map x1))\n  (a a_empty a_single a_concat)\n\n# merge : Map -> Map -> Map\nmerge = λa\n  let a_free = λb\n    let b_free = Free\n    let b_busy = Busy\n    let b_node = λb0 λb1 (Node b0 b1)\n    (b b_free b_busy b_node)\n  let a_busy = λb\n    let b_free = Busy\n    let b_busy = Busy\n    let b_node = λb0 λb1 0\n    (b b_free b_busy b_node)\n  let a_node = λa0 λa1 λb\n    let b_free = λa0 λa1 (Node a0 a1)\n    let b_busy = λa0 λa1 0\n    let b_node = λb0 λb1 λa0 λa1 (Node (merge a0 b0) (merge a1 b1))\n    (b b_free b_busy b_node a0 a1)\n  (a a_free a_busy a_node)\n\n# radix : u32 -> Map\nradix = λi λn λk λr switch i {\n  0: r\n  _: (radix i-1 n (* k 2) (swap (& n k) r Free))\n}\n\n# swap : u32 -> Map -> Map -> Map\nswap = λn switch n {\n  0: λx0 λx1 (Node x0 x1)\n  _: λx0 λx1 (Node x1 x0)\n}\n\n# main : u32\nmain = (sum (sort (gen 16 0)))\n"
  },
  {
    "path": "examples/sort_radix/main.hvm",
    "content": "@Busy = (* (a (* a)))\n\n@Concat = (a (b (* (* ((a (b c)) c)))))\n\n@Empty = (a (* (* a)))\n\n@Free = (a (* (* a)))\n\n@Node = (a (b (* (* ((a (b c)) c)))))\n\n@Single = (a (* ((a b) (* b))))\n\n@gen = (?((@gen__C0 @gen__C1) a) a)\n\n@gen__C0 = a\n  & @Single ~ a\n\n@gen__C1 = ({a d} ($([*2] {e $([+1] b)}) g))\n  & @Concat ~ (c (f g))\n  &! @gen ~ (a (b c))\n  &! @gen ~ (d (e f))\n\n@main = a\n  & @sum ~ (@main__C1 a)\n\n@main__C0 = a\n  & @gen ~ (16 (0 a))\n\n@main__C1 = a\n  & @sort ~ (@main__C0 a)\n\n@merge = ((@merge__C5 (@merge__C4 (@merge__C3 a))) a)\n\n@merge__C0 = (b (e (a (d g))))\n  & @Node ~ (c (f g))\n  &! @merge ~ (a (b c))\n  &! @merge ~ (d (e f))\n\n@merge__C1 = a\n  & @Node ~ a\n\n@merge__C2 = a\n  & @Node ~ a\n\n@merge__C3 = (a (b ((@merge__C1 ((* (* 0)) (@merge__C0 (a (b c))))) c)))\n\n@merge__C4 = ((@Busy (@Busy ((* (* 0)) a))) a)\n\n@merge__C5 = ((@Free (@Busy (@merge__C2 a))) a)\n\n@radix = (?(((* (* (a a))) @radix__C0) b) b)\n\n@radix__C0 = (a ({b $([&] $(d e))} ({$([*2] c) d} (f h))))\n  & @radix ~ (a (b (c (g h))))\n  & @swap ~ (e (f (@Free g)))\n\n@sort = (a c)\n  & @to_arr ~ (b (0 c))\n  & @to_map ~ (a b)\n\n@sum = ((0 ((a a) (@sum__C0 b))) b)\n\n@sum__C0 = (a (b d))\n  &! @sum ~ (a $([+] $(c d)))\n  &! @sum ~ (b c)\n\n@swap = (?((@swap__C0 @swap__C1) a) a)\n\n@swap__C0 = a\n  & @Node ~ a\n\n@swap__C1 = (* (b (a c)))\n  & @Node ~ (a (b c))\n\n@to_arr = (((* @Empty) (@to_arr__C1 (@to_arr__C0 a))) a)\n\n@to_arr__C0 = (a (d ({$([*2] $([+1] e)) $([*2] $([+0] b))} g)))\n  & @Concat ~ (c (f g))\n  &! @to_arr ~ (a (b c))\n  &! @to_arr ~ (d (e f))\n\n@to_arr__C1 = a\n  & @Single ~ a\n\n@to_map = ((@Free (@to_map__C1 (@to_map__C0 a))) a)\n\n@to_map__C0 = (a (c e))\n  & @merge ~ (b (d e))\n  &! @to_map ~ (a b)\n  &! @to_map ~ (c d)\n\n@to_map__C1 = (a b)\n  & @radix ~ (24 (a (1 (@Busy b))))\n"
  },
  {
    "path": "examples/stress/README.md",
    "content": "# stress\n\nThis is the basic stress-test used to test an implementation's maximum IPS. It\nrecursively creates a tree with a given depth, and then performs a recursive\ncomputation with a given length:\n\n```\ndef sum(n):\n  if n == 0:\n    return 0\n  else:\n    return n + sum(n - 1)\n\ndef fun(n):\n  if n == 0:\n    return sum(LENGTH)\n  else:\n    return fun(n - 1) + fun(n - 1)\n\nfun(DEPTH)\n```\n\nThis lets us test both the parallel and sequential performance of a runtime. For\nexample, by testing a tree of depth 14 and breadth 2^20, for example, we have\nenough parallelism to use all the 32k threads of a RTX 4090, and enough\nsequential work (1m calls) to keep each thread busy for a long time.\n"
  },
  {
    "path": "examples/stress/main.bend",
    "content": "def loop(n):\n  switch n:\n    case 0:\n      return 0\n    case _:\n      return loop(n-1)\n\ndef fun(n):\n  switch n:\n    case 0:\n      return loop(0x10000)\n    case _:\n      return fun(n-1) + fun(n-1)\n\ndef main:\n  return fun(8)\n"
  },
  {
    "path": "examples/stress/main.hvm",
    "content": "@fun = (?((@fun__C0 @fun__C1) a) a)\n\n@fun__C0 = a\n  & @loop ~ (65536 a)\n\n@fun__C1 = ({a b} d)\n  &! @fun ~ (a $([+] $(c d)))\n  &! @fun ~ (b c)\n\n@loop = (?((0 @loop__C0) a) a)\n\n@loop__C0 = a\n  & @loop ~ a\n\n@main = a\n  & @fun ~ (8 a)\n"
  },
  {
    "path": "examples/stress/main.js",
    "content": "function sum(n) {\n  if (n === 0) {\n    return 0;\n  } else {\n    return n + sum(n - 1);\n  }\n}\n\nfunction fun(n) {\n  if (n === 0) {\n    return sum(4096);\n  } else {\n    return fun(n - 1) + fun(n - 1);\n  }\n}\n\nconsole.log(fun(18));\n"
  },
  {
    "path": "examples/stress/main.py",
    "content": "def sum(n):\n  if n == 0:\n    return 0\n  else:\n    return n + sum(n - 1)\n\ndef fun(n):\n  if n == 0:\n    return sum(16)\n  else:\n    return fun(n - 1) + fun(n - 1)\n\nprint(fun(8))\n\n# Demo Micro-Benchmark / Stress-Test\n# \n# Complexity: 120,264,589,303 Interactions\n#\n# CPython: 640s on Apple M3 Max (1 thread) *\n# HVM-CPU: 268s on Apple M3 Max (1 thread)\n# Node.js: 128s on Apple M3 Max (1 thread) *\n# HVM-CPU:  14s on Apple M3 Max (12 threads)\n# HVM-GPU:   2s on NVIDIA RTX 4090 (32k threads)\n#\n# * estimated due to stack overflow\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "examples/sum_rec/main.bend",
    "content": "#flavor core\n\nsum = λn λx switch n {\n  0: x\n  _: let fst = (sum n-1 (+ (* x 2) 0))\n     let snd = (sum n-1 (+ (* x 2) 1))\n     (+ fst snd)\n}\n\nmain = (sum 20 0)\n"
  },
  {
    "path": "examples/sum_rec/main.hvm",
    "content": "@main = a\n  & @sum ~ (20 (0 a))\n\n@sum = (?(((a a) @sum__C0) b) b)\n\n@sum__C0 = ({c a} ({$([*2] $([+1] d)) $([*2] $([+0] b))} f))\n  &! @sum ~ (a (b $([+] $(e f))))\n  &! @sum ~ (c (d e))\n"
  },
  {
    "path": "examples/sum_rec/main.js",
    "content": "function sum(a, b) {\n  if (a === b) {\n    return a;\n  } else {\n    let mid = Math.floor((a + b) / 2);\n    let fst = sum(a, mid + 0);\n    let snd = sum(mid + 1, b);\n    return fst + snd;\n  }\n}\n\nconsole.log(sum(0, 10000000));\n"
  },
  {
    "path": "examples/sum_rec/sum.js",
    "content": "var sum = 0;\n\nfor (var i = 0; i < 2**30; ++i) {\n  sum += i;\n}\n\nconsole.log(sum);\n"
  },
  {
    "path": "examples/sum_tree/main.bend",
    "content": "gen = λd switch d {\n  0: λx x\n  _: λx ((gen d-1 (+ (* x 2) 1)), (gen d-1 (* x 2)))\n}\n\nsum = λd λt switch d {\n  0: 1\n  _: let (t.a,t.b) = t\n    (+ (sum d-1 t.a) (sum d-1 t.b))\n}\n\nmain = (sum 20 (gen 20 0))\n"
  },
  {
    "path": "examples/sum_tree/main.hvm",
    "content": "@gen = (?(((a a) @gen__C0) b) b)\n\n@gen__C0 = ({a d} ({$([*2] $([+1] b)) $([*2] e)} (c f)))\n  &! @gen ~ (a (b c))\n  &! @gen ~ (d (e f))\n\n@main = a\n  & @sum ~ (20 (@main__C0 a))\n\n@main__C0 = a\n  & @gen ~ (20 (0 a))\n\n@sum = (?(((* 1) @sum__C0) a) a)\n\n@sum__C0 = ({a c} ((b d) f))\n  &! @sum ~ (a (b $([+] $(e f))))\n  &! @sum ~ (c (d e))\n"
  },
  {
    "path": "examples/tuples/tuples.bend",
    "content": "type Tup8:\n  New { a, b, c, d, e, f, g, h }\n\nrot = λx match x {\n  Tup8/New: (Tup8/New x.b x.c x.d x.e x.f x.g x.h x.a)\n}\n\napp = λn switch n {\n  0: λf λx x\n  _: λf λx (app n-1 f (f x))\n}\n\nmain = (app 1234 rot (Tup8/New 1 2 3 4 5 6 7 8))\n"
  },
  {
    "path": "examples/tuples/tuples.hvm",
    "content": "@Tup8/New = (a (b (c (d (e (f (g (h ((0 (a (b (c (d (e (f (g (h i))))))))) i)))))))))\n\n@app = (?(((* (a a)) @app__C0) b) b)\n\n@app__C0 = (a ({b (c d)} (c e)))\n  & @app ~ (a (b (d e)))\n\n@main = b\n  & @app ~ (1234 (@rot (a b)))\n  & @Tup8/New ~ (1 (2 (3 (4 (5 (6 (7 (8 a))))))))\n\n@rot = ((@rot__C1 a) a)\n\n@rot__C0 = (h (a (b (c (d (e (f (g i))))))))\n  & @Tup8/New ~ (a (b (c (d (e (f (g (h i))))))))\n\n@rot__C1 = (?((@rot__C0 *) a) a)\n"
  },
  {
    "path": "paper/HVM2.typst",
    "content": "#import \"@preview/unequivocal-ams:0.1.0\": ams-article, theorem, proof\n#import \"@preview/cetz:0.2.2\": canvas, draw\n#import \"inet.typ\"\n\n#show link: underline\n#set cite(form: \"normal\", style: \"iso-690-author-date\")\n\n\n#show: ams-article.with(\n  title: [HVM2: A Parallel Evaluator for Interaction Combinators],\n  authors: ((name: \"Victor Taelin\", company: \"Higher Order Company\", email: \"taelin@HigherOrderCO.com\"),),\n  abstract:[\n    We present HVM2, an efficient, massively parallel evaluator for extended interaction combinators. When compiling non-sequential programs from a high-level programming language to C and CUDA, we achieved a near-ideal parallel speedup as a function of cores available (within a single device), scaling from 400 million interactions per second (MIPS) (Apple M3 Max; single thread), to 5,200 MIPS (Apple M3 Max; 16 threads), to 74,000 MIPS (NVIDIA RTX 4090; 32,768 threads). In this paper we describe HVM2's architecture, present snippets of the reference implementation in Rust, share early benchmarks and experimental results, and discuss current limitations and future plans.\n  ],\n  bibliography: bibliography(\"refs.bib\", style: \"annual-reviews\"),\n)\n\n*This paper is a work in progress. See the #link(\"https://github.com/HigherOrderCO/HVM\")[HVM repo] for the  latest version.*\n\n= Introduction\n\nInteraction Nets (IN's) @Lafont_1990 and Interaction Combinators (IC's) @Lafont_1997 were introduced by Lafont as a minimal and concurrent model of computation. Lafont proved that IC's were not only Turing Complete, but that they also preserve the complexity class and degree of parallelism. Moreover, Lafont argued that while Turing Machines are a universal model of sequential computation, IC's are a universal model of #emph[distributed] computation. The locality and strong confluence of Lafont's ICs make it suitable for massive parallel computation. This heavily implied that IC's are an optimal model of computation, in a very fundamental sense. Yet, it remained to be seen if this system could be implemented efficiently in practice.\n\nIn this paper, we answer this question positively. By storing Interaction Combinator nodes in a memory-efficient format, we're able to implement its core operations (annihilation, commutation, and erasure) as lightweight C procedures and CUDA kernels. Furthermore, by representing wires as atomic variables, we're able to perform interactions atomically, in a lock-free fashion and with minimal synchronization. We also extend our system with global definitions (for fast function applications) and native numbers (for fast numeric operations). The result, HVM2, is an efficient, massively parallel evaluator for ICs that achieves near-ideal speedup, up to at least 16,384 concurrent cores, peaking at 74 billion interactions per second on an NVIDIA RTX 4090.\n\nThis level of performance makes it compelling to propose HVM2 as a general\nframework for parallel computing. By translating constructs such as functions, algebraic data types, pattern matching, and recursion to HVM2, we see it as a potential compilation target for modern programming languages such as Python and Haskell. As a demonstration of this possibility, we also introduce #link(\"https://github.com/HigherOrderCO/bend\")[Bend], a high-level programming language that compiles to HVM2. We explain how some of these translations work, and set up a general framework to translate arbitrary languages, procedural or functional, to HVM2.\n\n#pagebreak()\n\n= Similar Works\n\n*Work In Progress*\n\n#pagebreak()\n\n= Syntax\n\nHVM2's syntax consists of an Interaction Calculus system which textually represents an Interaction Combinator system @Calculus_1999. This textual system is capable of representing any arbitrary Interaction Net, and it is therefore possible to represent \"vicious circles\" @Lafont_1997. We only consider HVM2 programs which do not contain any vicious circles.\n\nHVM2's syntax has seven different types of _agents_ (in Lafont's terms) or _Nodes_. Additionally, HVM2 also has _Variables_ to represent wires which connect ports across Nodes. _Trees_ are either Variables or Nodes. They are represented syntactically as:\n\n#align(center)[\n  ```\n<Node> ::=\n    | \"*\"                    -- (ERA)ser\n    | \"@\" <alphanumeric>     -- (REF)erence\n    | <Numeric>              -- (NUM)eric\n    | \"(\" <Tree> <Tree> \")\"  -- (CON)structor\n    | \"{\" <Tree> <Tree> \"}\"  -- (DUP)licator\n    | \"$(\" <Tree> <Tree> \")\" -- (OPE)rator\n    | \"?(\" <Tree> <Tree> \")\" -- (SWI)tch\n\n<Tree> ::=\n    | <alphanumeric>         -- (VAR)iable\n    | <Node>\n\n<alphanumeric> ::= [a-zA-Z0-9_.-/]+\n  ``` \\\n  _(For details on the `<Numeric>` syntax, see Numbers (@numbers))_\n] \\\n  \\\n\nNotice that Nodes form a tree-like structure, and throughout this document we will make systematic confusion between Nodes and Trees. For example, in Interactions (@interactions), it is critical to know when a Variable is permissible or not when referring to arbitrary Nodes. In Memory Layout (@memory), however, we purposefully blur the line between Nodes and Variables as the memory layout is greatly simplified by doing so.\n\nThe first three node types (`ERA`, `REF`, `NUM`) are nullary, and the last 4 types (`CON`, `DUP`, `OPE`, `SWI`) are binary. As implied above, `VAR` can be seen as an additional node type. As in Lafont's Interaction Nets, every node has an extra distinguished edge, called the main or principal port @Lafont_1997. Thus, nullary nodes have one port (one main and zero auxiliary), while binary nodes have three ports (one main and two auxiliary).\n\nWith the syntax above, the main port of the root node is \"free\", as it is not wired to another port. We can connect two main ports and form a reducible expression (redex), using the following syntax:\n\n#align(center)[\n  ```\n  <Redex> ::= <Tree> \"~\" <Tree>\n  ```\n]\n\nA Net consists of a root tree and a (possibly empty) list of `&`-separated redexes:\n\n#align(center)[\n  ```\n  <Net> ::= <Tree> (\"&\" <Redex>)*\n  ```\n]\n\n#pagebreak()\n\nHVM2 Nets represent what are known as _configurations_ in the literature. A Net like\n#align(center)[\n  ```\n  t1 & v1 ~ w1\n     & ..\n     & vn ~ wn\n  ```\n]\n\ngraphically represents a net like,\n\n#pad(1em)[\n  #figure(\n    image(\"configuration.png\", width: 50%),\n    caption: [\n      A configuration #footnote[This is a modified image\n of a configuration with multiple free main ports @Salikhmetov_2016.]\n    ],\n  )\n]\n\nwhere $omega$ is a wiring. Thus, HVM2 Nets contain only a single free main port. Wirings are possible between trees through pairs of `VAR` nodes with the same names. Note, however, that a variable can only occur twice. This aligns with Interaction Nets in that a wire can only connect two ports.\n\nLastly, an Book consists of a list of top-level definitions, or, \"named\" Nets:\n\n#align(center)[\n  ```\n  <Book> ::= (\"@\" <name> \"=\" <Net>)*\n  ```\n]\n\nEach `.hvm2` file contains a book, which is executed by HVM2. The entry point for HVM2 programs is the `@main` definition.\n\n== An Example\n\nThe following definition:\n#align(center)[\n  ```\n  @succ = ({(a b) (b R)} (a R))\n  ```\n]\n\nRepresents the HVM2 encoding of the $lambda$-calculus term `λs λz (s (s z))`, and can be drawn as the following Interaction Combinator net:\n/*\n          :\n         /_\\\n      ...: :...\n      :       :\n     /#\\     /_\\\n   ..: :..   a R\n  /_\\   /_\\  : :\n  a b...b R..:.:\n  :..........:\n*/\n#figure(caption: [An example $lambda$-calculus term.], canvas({\n  import draw: *\n\n  // inet.con(name: \"a\", pos: (0, 2), rot: -90deg)\n  // inet.con(name: \"b\", pos: (2, 0), rot: -180deg)\n  // inet.con(name: \"c\", pos: (0, -2), rot: 90deg)\n  inet.con(name: \"a\", pos: (0, 0), rot: 90deg)\n  inet.dup(name: \"b\", pos: (1.5, -1), rot: 90deg)\n  inet.con(name: \"c\", pos: (1.5, 1), rot: 90deg)\n  inet.con(name: \"d\", pos: (3, -2), rot: 90deg)\n  inet.con(name: \"e\", pos: (3, 0), rot: 90deg)\n  inet.link(\"a.0\", \"a.0\")\n\n  inet.link(\"a.1\", \"b.0\")\n  inet.link(\"a.2\", \"c.0\")\n  inet.link(\"b.1\", \"d.0\")\n  inet.link(\"b.2\", \"e.0\")\n\n  inet.link(\"d.2\", \"e.1\")\n  content((3.5, -1), [`b`])\n\n  // \"c.2\" -> \"e.2\"\n  inet.port(\"R\", (3.45, 1 + 1/6), -90deg)\n  inet.port(\"R'\", (3.45, 1 + 1/6), 90deg)\n  inet.link(\"e.2\", \"R\")\n  inet.link(\"c.2\", \"R'\")\n  content((3, 1.5), [`R`])\n\n  // \"c.1\" -> \"d.1\"\n  inet.port(\"A1\", (4.1, -1), -180deg)\n  inet.port(\"A2\", (4.1, 0), 0deg)\n  inet.port(\"A3\", (3.45, 1 - 1/6), 90deg)\n  inet.port(\"A3'\", (3.45, 1 - 1/6), -90deg)\n  inet.link(\"d.1\", \"A1\")\n  inet.link(\"A1\", \"A2\")\n  inet.link(\"A2\", \"A3'\")\n  inet.link(\"c.1\", \"A3\")\n  content((4.4, -0.5), [`a`])\n}))\n\nNotice how `CON`/`DUP` nodes in HVM2 correspond directly to constructor and duplicator nodes in Lafont's Interaction Combinators @Lafont_1997. Aux-to-main wires are implicit through the tree-like structure of the syntax, while aux-to-aux wires are explicit through variable nodes, which are always paired.\n\nAdditionally, main ports being implicit is critical to storing nodes efficiently in memory. Nodes can be represented as just two ports (the HVM2 memory model for wires) rather than three. In HVM2, since every port is 32 bits, this allows us to store a single node in a 64-bit word. This compact representation lets us use built-in atomic operations in various parts of the code, which was key to making parallel C and CUDA versions efficient. For details on the precise memory representation, see @architecture.\n\n== Interpretation of the Syntax\n\nSemantically, `CON`, `DUP`, and `ERA` nodes correspond accordingly to Lafont's #emph[constructor], #emph[duplicator], and #emph[eraser] symbols @Lafont_1997, and behave like Mazza's Symmetric Interaction Combinators @Mazza_2007. The `VAR` node represents a wiring in the graph, connecting two ports of a Net. They are linear and paired in the sense that (except for the free main port) every port is connected to exactly one other port, and therefore each variable occurs exactly twice.\n\n`REF` nodes are an extension to Lafont's IC's, and they represent an immutable net that is expanded in a single interaction. While not essential for the expressivity of the system, `REF` nodes are essential for performance, as they enable fast global functions, a degree of laziness in a strict setup (critical to making GPU implementations viable), and allow us to represent tail recursion in constant space.\n\n`NUM`, `OPE` and `SWI` nodes are also not essential expressivity-wise, but are too important for performance reasons. Modern processors are equipped with native machine integer operations. Emulating these operations with IC constructs analogous to Church or Scott Numerals would be very inefficient. Thus, these numeric nodes are necessary for HVM2 to be efficient in practice.\n\n#pagebreak()\n\n= Interactions <interactions>\n\nThe AST above specifies HVM2's data format. As a virtual machine, it also provides a mechanism to compute with that data. In traditional VMs, these are called *instructions*. In term rewriting systems, there are usually *reductions*. In HVM2, the mechanism for computation is called *interactions*. There are ten of them. All interactions are listed below using Gentzen-style rules (redexes in the line above reduce to ones in the line below).\n\n#v(1em)\n$\n  & \"Arbitrary Nodes (including\" #raw(\"VAR\") \")\" #h(2em) & #raw(\"A\"), #raw(\"B\"), #raw(\"C\"), #raw(\"D\") &:= #raw(\"<Tree>\") \\\n  & \"Binary Nodes\" #footnote[In the interaction rules `()` and `{}` refer to arbitrary binary nodes, not just `CON` and `DUP`.] & #raw(\"()\"), #raw(\"{}\") &:= #raw(\"CON\") | #raw(\"DUP\") | #raw(\"OPA\") | #raw(\"SWI\") \\\n  & \"Nullary Nodes\" & circle.filled.small, circle.stroked.small &:= #raw(\"ERA\") | #raw(\"REF\") | #raw(\"NUM\") \\\n  & \"Numeric Nodes\" & #raw(\"N\"), #raw(\"M\") &:= #raw(\"NUM\") \"(Numbers or Operations)\"\\\n  & \"Numeric Value Nodes (Not Operators)\" & #raw(\"#n\"), #raw(\"#m\") &:= #raw(\"NUM\") \"where\" #raw(\"n\"), #raw(\"m\") in bb(Q) \\\n  & \"Erasure Nodes\" & #raw(\"*\") &:= #raw(\"ERA\") \\\n  & \"Variables\" & #raw(\"x\"), #raw(\"y\"), #raw(\"z\"), #raw(\"w\") &:= #raw(\"VAR\") \\\n$\n\n#v(1em)\n#v(1em)\n#show math.equation: set text(15pt)\n#grid(\n  align: center,\n  columns: (1fr, 1fr),\n  gutter: 1pt,\n  [\n    #smallcaps[(#strong[link])]\n    #math.frac(\n      [\n        `B` contains `x` \\\n        `x ~ A`\n      ],\n      [`B[x` $arrow.l$ `A]`]\n    )\n  ],\n  [\n    #smallcaps[(#strong[call])]\n    #math.frac(\n      [\n      `A` is not a `VAR` node\n      #v(1em) \\\n      `@foo ~ A`\n    ],\n    `expand(@foo) ~ A`)\n  ],\n\n)\n#v(1em)\n#grid(\n  align: center,\n  columns: (1fr, 1fr),\n  gutter: 1pt,\n  [\n    #smallcaps[(#strong[void])]\n    #math.frac([$circle.filled.small$ `~` $circle.stroked.small$], ``)\n  ],\n  [\n    #smallcaps[(#strong[eras]e)]\n    #math.frac([$circle.filled.small$ `~ (A B)`],\n      [\n        $circle.filled.small$ `~ A` \\\n        $circle.filled.small$ `~ B`\n      ])\n  ],\n)\n#v(1em)\n#grid(\n  align: center,\n  columns: (1fr, 1fr),\n  gutter: 1pt,\n  [\n    #smallcaps[(#strong[comm]ute)]\n    #math.frac(`(A B) ~ {C D}`,\n      ```\n      {x y} ~ A\n      {z w} ~ B\n      (x z) ~ C\n      (y w) ~ D\n      ```\n    )\n  ],\n  [\n    #smallcaps[(#strong[anni]hilate)]\n    #math.frac(\n      `(A B) ~ (C D)`,\n      ```\n      A ~ C\n      B ~ D\n      ```\n    )\n  ],\n\n)\n#v(1em)\n#grid(\n  align: center,\n  columns: (1fr, 1fr),\n  gutter: 1pt,\n[\n    #smallcaps[(#strong[oper]ate 1)]\n    #math.frac(`N ~ $(M A)`, `op(N, M) ~ A`)\n  ],\n  [\n    #smallcaps[(#strong[swit]ch 1)]\n    #math.frac(`#0 ~ ?(A B)`, `A ~ (B *)`)\n\n  ],\n\n)\n#v(1em)\n#grid(\n  align: center,\n  columns: (1fr, 1fr),\n  gutter: 1pt,\n  [\n    #smallcaps[(#strong[oper]ate 2)]\n    #math.frac(\n      [\n        `A` is not a `NUM` node\n        #v(1em) \\\n        `N ~ $(A B)`\n      ],\n      `A ~ $(N B)`\n    )\n  ],\n  [\n    #smallcaps[(#strong[swit]ch 2)]\n    #math.frac(`#n+1 ~ ?(A B)`, `A ~ (* (#n B))`)\n  ],\n)\n#v(2em)\n#show math.equation: set text(10pt)\n\nNote that rules are #emph[symmetric]: if a rule applies to a redex `A ~ B` then it also applies to `B ~ A`. Explanations and implementation details of each of the rules follow.\n\n== Link\n\"Links\" two ports where at least one is a `VAR` Node. A *global substitution* is performed replacing the single other occurrence of `x` with `A`. Recall that there is _exactly_ one other occurrence of `x` in the net. In the graph-rewriting system of Interaction Nets, linking two ports isn't technically an interaction, as wires are not named. However, for the term-rewriting calculus, wires are named and must be substituted for. When applying this rule, the redex `x ~ A` is removed, and the occurrence of `x` in `B` node is replaced with `A`. This is the only rule where nodes \"far apart\" can affect each other. See <linker> for details on the `link` function.\n\n== Call\nExpands a `REF`, replacing it with its definition. The definition is essentially copied from the static Book to the global memory, allocating its nodes and creating fresh variables. This operation is key to enable fast function application, since, without it, one would need to use duplicator nodes for the same purpose, which brings considerable overhead. It also introduces some laziness in a strict evaluator, allowing for global recursive functions, and constant-space tail-calls.\n\n== Void\nErases two nullary nodes connected to each other. The result is nothing: both nodes are consumed, fully cleared from memory. The `VOID` rule completes a garbage collection process.\n\n== Erase\nErases a binary node `(A B)` connected to an nullary node, propagating the nullary node towards both nodes `A` and `B`. The rule performs a granular, parallel garbage collection of nets that go out of scope.\n\nWhen the nullary node is a `NUM` or a `REF`, the #smallcaps[erase] rule actually behaves as a copy operation, cloning the `NUM` or `REF`, and connecting to both ports. #emph[However], when a copy operation is applied to a `REF` which contains `DUP` nodes, it instead is computed as a normal #smallcaps[call] operation. This allows us to perform fast copy of \"function pointers\", while still preserving Interaction Combinator semantics.\n\n== Commute\nCommutes two binary nodes of different types, essentially cloning them. The #smallcaps[commute] rule can be used to clone data and to perform loops and recursion, although these are preferably done via #smallcaps[call]s: Cloning large networks is faster through the #smallcaps[call] interaction, as it can be done in a single pass, as opposed to the incremental #smallcaps[commute] interactions that would have to propagate throughout the network.\n\n== Annihilate\nAnnihilates two binary nodes of the same type connected to each-other, replacing them with two redexes. The #smallcaps[annihilate] rule is the most essential computation rule, and is used to implement beta-reduction and pattern-matching.\n\n== Operate\nPerforms a numeric operation between two `NUM` nodes `N` and `M` connected by an `OPE` node. Note that `N` and `M` should not both be numeric values for this interaction to perform a sensible numeric operation. Dispatching to different native numeric operations depends on the `N` and `M` nodes themselves. See Numbers (@numbers) for details.\n\nNote than when counting the number interactions, #smallcaps[operate 2] is not counted, as this would cause the number of interactions to be non-deterministic.\n\n== Switch\nPerforms a switch on a `NUM` node `#n` connected to a `SWI` node, treating it like a `Nat ::= Zero | (Succ pred)`. Here, `A` is expected to be a #highlight[tuple (first reference of the term \"tuple\", it's unclear what the encoding is)] with both cases: `zero` and `succ`, and `B` is the return port. If `n` is 0, we return the `zero` case, and erase the `succ` case. Otherwise, we return the `succ` case applied to `n-1`, and erase the `zero` case.\n\n#pagebreak()\n\n== Interaction Table\n\nSince there are eight node types, there is a total of 64 possible pairwise node interactions. The interaction rule for an active pair is _uniquely_ determined by the types of the two nodes in the pair. The table below shows which interaction rule is triggered for each possible pair of nodes that form a redex. Since the interaction rules are symmetric, this table is symmetric across the main diagonal. #highlight[`CON`-`SWI` being #smallcaps[comm] is problematic; should be removed or the reduction for #smallcaps[swit] should change].\n#v(1em)\n\n#align(center)[\n  ```\n  | A\\B |  VAR |  REF |  ERA |  NUM |  CON |  DUP |  OPR |  SWI |\n  |-----|------|------|------|------|------|------|------|------|\n  | VAR | LINK | LINK | LINK | LINK | LINK | LINK | LINK | LINK |\n  | REF | LNIK | VOID | VOID | VOID | CALL | ERAS | CALL | CALL |\n  | ERA | LINK | VOID | VOID | VOID | ERAS | ERAS | ERAS | ERAS |\n  | NUM | LINK | VOID | VOID | VOID | ERAS | ERAS | OPER | SWIT |\n  | CON | LINK | CALL | ERAS | ERAS | ANNI | COMM | COMM | COMM |\n  | DUP | LINK | ERAS | ERAS | ERAS | COMM | ANNI | COMM | COMM |\n  | OPR | LINK | CALL | ERAS | OPER | COMM | COMM | ANNI | COMM |\n  | SWI | LINK | CALL | ERAS | SWIT | COMM | COMM | COMM | ANNI |\n  ```\n]\n\n\\\n\nBecause for each active pair exactly one rule applies, HVM2 retains the same _strong confluence_ that Lafont's Interaction Combinators do @Lafont_1997. This implies that not only can HVM2 programs be reduced completely in parallel, but also that the number of reductions is invariant to the order in which interaction rules are applied. This ensures that HVM2 can reduce redexes in any order without any risk of complexity blowups.\n\n#pagebreak()\n\n= Substitution Map & Atomic Linker <linker>\n\nWhile HVM2 retains the strong confluence property of Lafont's IC's, _locality_ is more difficult to obtain. This is due to HVM2's variables. Variables link two different parts of the program, and thus can cause interference when two threads attempt to reduce two redexes in parallel. For example, consider a subset of a Net:\n\n#v(1em)\n#align(center)[\n  ```\n  & (a b) ~ (d c)\n  & (c d) ~ (f e)\n  ```\n]\n#v(1em)\n\nTwo threads attempting to reduce this Net can be represented as follows:\n\n#v(1em)\n#align(center)[\n  ```\n       Thread_0     Thread_1\n  --a--|\\____/|--c--|\\____/|--e--\n  --b--|/    \\|--d--|/    \\|--f--\n  ```\n]\n#v(1em)\n\nNotice that in the reduction of these two redexes, both `Thread_0` and `Thread_1` will need to access variables `c` and `d`. This requires synchronization.\n\nIn HVM2, there is a global collection of redexes that is mutated in parallel by a variety of threads. See Architecture (@architecture) for details. Since every variable occurs exactly twice, the #smallcaps[link] interaction with a variable `x` will also occur twice, but _possibly at very different times_. The first time is when this variable is first encountered, and somehow a substitution must be \"deferred\" until the #smallcaps[link] interaction rule is applied to the second occurrence of `x`.\n\nThis is accomplished by a global, atomic substitution map, which tracks these deferred substitutions. When a variable is linked to a node, or to another variable, it is inserted into the substitution map. When that same variable is linked again, it will already have an entry in the substitution map, and then the proper redex will be constructed.\n\nThe substitution map can be represented efficiently with a flat buffer, where the index is the variable name, and the value is the node that has been substituted. This can be done atomically, via a simple lock-free linker. In pseudocode, this roughly looks like:\n\n#pagebreak()\n\n#align(center)[\n```python\n# Attempts to link A and B.\ndef link(subst: Dict[str, Node], A: Node, B: Node):\n    while True:\n        # If A is not a VAR: swap A and B, and continue.\n        if type(A) != VAR:\n            swap(A, B)\n\n        # If A is not a VAR: both are non-vars. Create a new redex.\n        if type(A) != VAR:\n            push_redex(A, B)\n\n        # Here, A is a VAR. Create a `A: B` entry in the map.\n        got: Port = subst.set_atomic(A, B)\n\n        # If there was no `A` entry, stop.\n        if got is None:\n            break\n\n        # Otherwise, delete `A` and link `got` to `B`.\n        del subst[A]\n        A = got\n```\n]\n#v(1em)\n\nTo see how this algorithm works, let's consider, again, the scenario above:\n\n#v(1em)\n#align(center)[\n  ```\n       Thread_0     Thread_1\n  --a--|\\____/|--c--|\\____/|--e--\n  --b--|/    \\|--d--|/    \\|--f--\n  ```\n]\n#v(1em)\n\nAssume we start with a substitution `a` $arrow.l$ `#42`, and let both threads reduce a redex in parallel. Each thread  are an `ANNI` rule, their effect is to link both ports; thus, the resulting wire connected to `#42` (with the wires `a`, `d`, and `e` labelled for clarity) should be\n\n#v(1em)\n#align(center)[\n  ```\n  #42 ---a---.         .---e---\n              '---d---'\n  ```\n]\n#v(1em)\n\nThat is, `e` must be directly linked to `#42`. Let's now evaluate the algorithm in an arbitrary order, step-by-step. Recall that the initial Net is:\n\n#v(1em)\n#align(center)[\n  ```\n  & (a b) ~ (d c)\n  & (c d) ~ (f e)\n  ```\n]\n#v(1em)\n\nAnd for simplicity we're observing only ports `a`, `d`, and `e`. `Thread_0` will attempt to perform `link(a, d)` and `Thread_1` will attempt `link(d, e)`. There are many possible orders of execution:\n\n#pagebreak()\n\n== Possible Execution Order 1\n\n#pad(left: 2em)[\n  ```\n  - a: #42\n  ======= Thread_2: link(d, e)\n  - a: #42\n  - d: e\n  ======= Thread_1: link(a, d)\n  - a: d\n  - d: e\n  ======= Thread_1: got `a: #42`, thus, delete `a` and link(d, #42)\n  - d: #42\n  ======= Thread_1: got `d: e`, thus, delete `d` and link(e, #42)\n  - e: #42\n  ```\n]\n\nThe resulting substitution map is linking `e` to `42`, as required.\n\n== Possible Execution Order 2\n\n#pad(left: 2em)[\n  ```\n  - a: #42\n  ======= Thread_1: link(d, a)\n  - a: #42\n  - d: a\n  ======= Thread_2: link(d, e)\n  - a: #42\n  - d: e\n  ======= Thread_2: got `d: a`, thus, delete `d` and link(a, e)\n  - a: e\n  ======= Thread_2: got `a: #42`, thus, delete `a` and link(e, #42)\n  - e: 42\n  ```\n]\n\nThe resulting substitution map is linking, again, `e` to `42`, as required.\n\n== Possible Execution Order 3\n\n#pad(left: 2em)[\n  ```\n  - a: #42\n  ======= Thread_1: link(d, a)\n  - a: #42\n  - d: a\n  ======= Thread_2: link(e, d)\n  - a: #42\n  - d: a\n  - e: d\n  ```\n]\n\nIn this case, the result isn't directly linking `e` to `#42`. But it does link `e` to `d`, which links to `a`, which links to `#42`. Thus, `e` is, indirectly, linked to `#42`. While it does temporarily use more memory in this case, it is, semantically, the same result. Additionally, the indirect links will be cleared as soon as `e` is linked to something else. It is easy enough to see that this holds for all possible evaluation orders.\n\n#pagebreak()\n\n= Numbers <numbers>\nHVM2 has a built-in support for 32-bit numerics and operations represented by the `NUM` node type. `NUM` nodes in HVM2 have a 5-bit tag and a 24-bit payload. Depending on the tag, numbers can represent unsigned integers (U24), signed integers (I24), IEEE 754 binary32 floats (F24), or (possibly partially applied) operators. These choices mean any numeric node can be represented in 29 bits, which can be unboxed in a 32-bit port with a 3 bit tag for the node type.\n\n== Syntax\n\nNumeric nodes can either be a number or a (possibly partially applied) operator. Syntactically,\n\n#align(center)[\n  ```\n  <Numeric> ::=\n      | <Number>\n      | <Operator>\n\n  <Number> ::=\n      | <Nat>\n      | <Int>\n      | <Float>\n\n  <Operator> ::=\n      | \"[\" <Operation> \"]\"           -- (unapplied)\n      | \"[\" <Operation> <Number> \"]\"  -- (partially applied)\n\n  <Operation> ::=\n      | \"+\"   -- (ADD)\n      | \"-\"   -- (SUB)\n      | \"*\"   -- (MUL)\n      | \"/\"   -- (DIV)\n      | \"%\"   -- (REM)\n      | \"=\"   -- (EQ)\n      | \"!\"   -- (NEQ)\n      | \"<\"   -- (LT)\n      | \">\"   -- (GT)\n      | \"&\"   -- (AND)\n      | \"|\"   -- (OR)\n      | \"^\"   -- (XOR)\n      | \">>\"  -- (SHR)\n      | \"<<\"  -- (SHL)\n      | \":-\"  -- (FP_SUB)\n      | \":/\"  -- (FP_DIV)\n      | \":%\"  -- (FP_REM)\n      | \":>>\" -- (FP_SHR)\n      | \":<<\" -- (FP_SHL)\n  ```\n]\n\nwhere `<Int>` is disambiguated from `<Nat>` by requiring a sign prefix `+`/`-`, and flipped versions of non-commutative operators (`FP_*`) are provided for convenience.\n\n#pagebreak()\n\n== Numeric Node Memory Layout\n\nIn order to understand how numeric operations are derived from the numeric nodes, we must look at the memory layout of the different numeric values and operations. As stated above, numeric nodes fit into 29 bits, so they can be inlined into a 32-bit ports. Numeric nodes have the following layout\n\n#align(center)[\n  ```\n  VVVVVVVVVVVVVVVVVVVVVVVVTTTTT\n  ```\n]\n\nWhere the 5-bit tag `T` has 19 possible values: one of the three number types (`U24`, `I24`, `F24`), one of the 15 operations, or a special value `SYM`. The 24-bit value `V` has a few possible interpretations:\n- If $#raw(\"T\") in {#raw(\"U24\"), #raw(\"I24\"), #raw(\"F24\")}$, then `V` is interpreted as a number with the type `T`. For example: `123`, `-123`, `1.0`.\n- If $#raw(\"T\") in #raw(\"<Operation>\")$, then `V` is an untyped number. This is a partially applied operation. The type of the second argument (when applied) will dictate the interpretation of `V`. For example: `[/123]`, `[*-123]`, `[%1.0]`.\n- If $#raw(\"T\") = #raw(\"SYM\")$, then $#raw(\"V\") in #raw(\"<Operator>\")$. This is an unapplied operator, like `[+]`.\n\n== U24 - Unsigned 24-bit Integer\n\nU24 numbers represent unsigned integers from 0 to 16,777,215 ($2^24 - 1$).\n\nThe 24-bit payload directly encodes the integer value. For example:\n\n#align(center)[\n  ```\n  0000 0000 0000 0000 0000 0001 = 1\n  0000 0000 0000 0000 0000 0010 = 2\n  1111 1111 1111 1111 1111 1111 = 16,777,215\n  ```\n]\n\n== I24 - Signed 24-bit Integer\n\nI24 numbers represent signed integers from -8,388,608 to 8,388,607.\n\nThe 24-bit payload uses two's complement encoding. For example:\n\n#align(center)[\n  ```\n  0000 0000 0000 0000 0000 0000 = 0\n  0000 0000 0000 0000 0000 0001 = 1\n  0111 1111 1111 1111 1111 1111 = 8,388,607\n  1000 0000 0000 0000 0000 0000 = -8,388,608\n  1111 1111 1111 1111 1111 1111 = -1\n  ```\n]\n\n== F24 - 24-bit IEEE 754 binary32 Float\n\nF24 numbers represent a subset of IEEE 754 binary32 floating point numbers; it supports approximately the same range, but with less precision. The 24-bit payload is laid out as follows:\n\n#align(center)[\n  ```\n  SEEE EEEE EMMM MMMM MMMM MMMM\n  ```\n]\n\nWhere:\n- S is the sign bit (1 = negative, 0 = positive)\n- E is the 8-bit exponent, with a bias of 127 (range of −126 to +127)\n- M is the 15-bit significand (mantissa) precision\n\nThe value is calculated as:\n- If E = 0 and M = 0, the value is signed zero\n- If E = 0 and M $eq.not$ 0, the value is a subnormal number: \\\n  $(-1)^S times 2^(-126) times (0.M \"in base 2\")$\n- If 0 < E < 255, the value is a normal number: \\\n  $(-1)^S times 2^(E-127) times (1.M \"in base 2\")$\n- If E = 255 and M = 0, the value is signed infinity\n- If E = 255 and M $eq.not$ 0, the value is NaN (Not-a-Number)\n\nF24 supports a range of approximately $plus.minus 3.4 times 10^38$. The smallest positive normal number is $2^(-126) approx 1.2 times 10^(-38)$, while the smallest subnormal numbers go down to $2^(-141) approx 3.6 times 10^(-43)$.\n\n== Numeric Operations\n\nWhen two `NUM` nodes are connected by an `OPE` node, as shown in Interaction Rules (@interactions), a numeric operation is performed using the `op` function. The operation to be performed depends on the tags of each numeric node.\n\nSome operations `op(N, M)` are invalid, and simply return `0`:\n- If both numeric tags are types.\n- If both numeric tags are operations.\n- If both numeric tags are `SYM`.\n\nOtherwise:\n- If one of the tags is `SYM`, the output has the tag represented by the `SYM` numeric node and the payload of the other operand. For example,\n\n#align(center)[\n  ```\n  OP([+], 10) = [+10]\n  OP(-1, [*]) = [*0xffffff]\n  ```\n]\n\n- If one of the tags is an operation, and the other is a type, a native\n  operation is performed, according to the following table:\n\n#align(center)[\n  ```\n  |   | U24 | I24 | F24   |\n  |---|-----|-----|-------|\n  |ADD| +   | +   | +     |\n  |SUB| -   | -   | -     |\n  |MUL| *   | *   | *     |\n  |DIV| /   | /   | /     |\n  |REM| %   | %   | %     |\n  |EQ | ==  | ==  | ==    |\n  |NEQ| !=  | !=  | !=    |\n  |LT | <   | <   | <     |\n  |GT | >   | >   | >     |\n  |AND| &   | &   | atan2 |\n  |OR | |   | |   | log   |\n  |XOR| ^   | ^   | pow   |\n  |SHR| >>  |     |       |\n  |SHL| <<  |     |       |\n  ```\n]\nWhere empty cells are intentionally left unspecified.\n\nThe resulting number type is the same as the input type, except for comparison operators (`EQ`, `NEQ`, `LT`, `GT`) which always return `U24` `0` or `1`.\n\n#pagebreak()\n\nThe number tagged with the operation is the left operand of the native operation, and the number tagged with the type is the right operand. For example,\n\n#align(center)[\n  ```\n  op([/1.2], 3.0) = op(3.0, [/1.2]) = 1.2 / 3.0\n  ```\n]\n\nNote that this means that the number type used in an operation is always determined by the right operand; if the left operand is of a different type, its bits will be reinterpreted.\n\nFinally, flipped operations (such as `FP_SUB`) interpret their operands in the opposite order (e.g. `SUB` represents `a - b` whereas `FP_SUB` represents `b - a`). This allows representing e.g. both `1 - x` and `x - 1` with partially-applied operations (`[-1]` and `[:-1]` respectively).\n\n#align(center)[\n```\nOP([-2], +1) = +1\nOP([:-2], 1) = -1\n```\n]\n\nNote that `op` is a symmetric function (since the order of the operands is determined by their tags). That is, `op(N, M) = op(M, N)`. This makes the \"swap\" interaction in #smallcaps[operate 2] valid.\n\n#pagebreak()\n\n= The 32-bit Architecture <architecture>\n\nThe initial version of HVM2, as implemented in this paper, is based on a 32-bit architecture. In this section, we'll use snippets of the Rust reference implementation of the interpreter, available at the #link(\"https://github.com/HigherOrderCO/hvm2\")[HVM2 repo], to document this architecture.\n\n== Memory Layout <memory>\n\nPorts are 32-bit values that represent a wire connected to a main port. The low 3-bits are reserved to identify the type of the node (`VAR`, `REF`, `ERA`, etc) whose main port the wire is connected to. This is a port's _tag_. The upper 29-bits hold a port's _value_. The interpretation of the value is dependent on the tag. The value is either an address (for binary `CON`, `DUP`, `OPR`, and `SWI` nodes), a virtual function address (for `REF` nodes), an unboxed 29-bit number (for `NUM` nodes), a variable name (for `VAR` nodes), or `0` (for `ERA` nodes). Binary nodes are represented in memory as a pair of two ports. Notice that _ports store the kind of node they are connecting to_, nodes don't store their own type.\n\n#align(center)[\n```rust\npub type Tag = u8;  // 3 bits  (rounded up to u8)\npub type Val = u32; // 29 bits (rounded up to u32)\n\npub struct Port(pub u32); //  Tag + Val  (32 bits)\npub struct Pair(pub u64); // Port + Port (64 bits)\n```\n]\n\nThe Global Net structure includes three shared memory buffers, `node`: a node buffer where nodes are allocated, `vars`: a buffer representing the current substitution map (with the 29-bit key being the variable name, and the value being the current substitution), and `rbag`: a collection of active redexes. `APair` and `APort` are atomic variants of `Pair` and `Port`, respectively,\n\n#align(center)[\n```rust\npub struct GNet<'a> {\n  pub node: &'a mut [APair],\n  pub vars: &'a mut [APort],\n  pub rbag: &'a mut [APair],\n}\n```\n]\n\nSince this 32-bit architecture has 29-bit values, that means we can address a total of $2^29$ nodes and variables, making the `node` buffer at most `4 GB` long, and the `vars` buffer at most `2 GB` long. A 64-bit architecture would increase this limit to match the overwhelming majority of use cases, and will be incorporated in a future revision of the HVM2 runtime.\n\nSince top-level definitions are just static nets, they are stored in a similar structure, with the key difference that they include an explicit `root` port, which, is used to connect the expanded definition its target port in the graph. We also include a `safe` flag, which indicates whether this definition has duplicator nodes or not. This affects the `DUP-REF` interaction, which will just copy the `REF` port, rather than expanding the definition, when it is `safe`.\n\n#align(center)[\n```rust\npub struct Def {\n  pub safe: bool,      // has no dups\n  pub root: Port,      // root port\n  pub rbag: Vec<Pair>, // def redex bag\n  pub node: Vec<Pair>, // def node buffer\n}\n```\n]\n\nFinally, the global Book is just a map of names to `Def`s:\n\n#align(center)[\n```rust\npub struct Book {\n  pub defs: Vec<Def>,\n}\n```\n]\n\nNotice that, like variables, names are simply indexes into the global `Book`.\n\nThis concludes HVM2's memory layout. For more details, check the reference Rust implementation in the #link(\"https://github.com/HigherOrderCO/hvm2\")[HVM2 repo].\n\n== Example Interaction Net Layout\n\nConsider, again, the following net:\n\n#align(center)[\n```\n(a b)\n& (b a) ~ (x (y *))\n& {y x} ~ @foo\n```\n]\n\nIn HVM2's memory, it would be represented as:\n\n#align(center)[\n```\nRBAG | FST-TREE | SND-TREE\n---- | -------- | --------\n0800 | CON 0002 | CON 0003 // '& (b a) ~ (x (y *))'\n1800 | DUP 0005 | REF 0000 // '& {x y} ~ @foo'\n---- | -------- | --------\nNODE | PORT-1   | PORT-2\n---- | -------- | --------\n0001 | VAR 0000 | VAR 0001 // '(a b)' node (root)\n0002 | VAR 0001 | VAR 0000 // '(b a)' node\n0003 | VAR 0002 | CON 0004 // '(x (y *))' node\n0004 | VAR 0003 | ERA 0000 // '(y *)' node\n0005 | VAR 0003 | VAR 0002 // '{y x}' node\n---- | -------- | --------\nVARS | VALUE    |\n---- | -------- |\nFFFF | CON 0001 | // points to root node\n```\n]\n\nNote that the `VARS` buffers has only one entry, because there are no substitutions, but we always use the last variable to represent the root port, serving as an entry point to the graph.\n\n== Example Interaction\n\nInteractions can be implemented in five steps:\n1. Allocate the needed resources.\n\n2. Loads nodes from global memory to registers.\n\n3. Initialize fresh variables on the substitution map.\n\n4. Stores fresh nodes on the node buffer.\n\n5. Atomically links outgoing wires.\n\nFor example, the #smallcaps[commute] interaction is implemented in Rust as:\n\n#align(center)[\n```rust\npub fn interact_comm(&mut self, net: &GNet, a: Port, b: Port) -> bool {\n  // Allocates needed resources.\n  if !self.get_resources(net, 4, 4, 4) {\n    return false;\n  }\n\n  // Loads nodes from global memory.\n  let a_ = net.node_take(a.get_val() as usize);\n  let a1 = a_.get_fst();\n  let a2 = a_.get_snd();\n  let b_ = net.node_take(b.get_val() as usize);\n  let b1 = b_.get_fst();\n  let b2 = b_.get_snd();\n\n  // Stores new vars.\n  net.vars_create(self.v0, NONE);\n  net.vars_create(self.v1, NONE);\n  net.vars_create(self.v2, NONE);\n  net.vars_create(self.v3, NONE);\n\n  // Stores new nodes.\n  net.node_create(self.n0, pair(port(VAR, self.v0), port(VAR, self.v1)));\n  net.node_create(self.n1, pair(port(VAR, self.v2), port(VAR, self.v3)));\n  net.node_create(self.n2, pair(port(VAR, self.v0), port(VAR, self.v2)));\n  net.node_create(self.n3, pair(port(VAR, self.v1), port(VAR, self.v3)));\n\n  // Links.\n  self.link_pair(net, pair(port(b.get_tag(), self.n0), a1));\n  self.link_pair(net, pair(port(b.get_tag(), self.n1), a2));\n  self.link_pair(net, pair(port(a.get_tag(), self.n2), b1));\n  self.link_pair(net, pair(port(a.get_tag(), self.n3), b2));\n\n  return true;\n}\n```\n]\n#v(1em)\n\nNote that, other than the linking, all operations here are local. Taking nodes\nfrom global memory is safe, because the thread that holds a redex implicitly\nowns both trees it contains, and storing vars and nodes is safe, because these\nspaces have been allocated by the thread. A fast concurrent allocator for small\nvalues is assumed. In HVM2, we just use a simple linear bump allocator, which is\nfast and fragmentation-free in a context where all allocations are at most\n64-bit values (the size of a single node).\n\n= Massively Parallel Evaluation\n\nProvided the architecture we just constructed, evaluating an HVM2 program in\nparallel is surprisingly easy: *just compute global redexes concurrently, until\nthere is no more work to do*.\n\nHVM2's local interactions exposes the original program's full degree of\nparallelism, ensuring that every work that *can* be done in parallel *will* be\ndone in parallel. In other words, it maximizes the theoretical speedup, per\nAmdahl's law. The atomic linking procedure ensures that points of\nsynchronization that emerge from the original program are solved safely and\nefficiently, without no room for race conditions. Finally, the strong confluence\nproperty ensures that the total work done is independent of the order that\nredexes are computed, giving us freedom to evaluate in parallel without\ngenerating extra work.\n\n== Redex Sharing\n\nAn additional question, is, how do we actually distribute that workload through\nall cores of a modern processor? The act of sharing a redex is, itself, a point\nof synchronization. If this is done without enough caution, it can result in\ncontention, and slowing up execution. HVM2 solves this by two different\napproaches:\n\nOn _CPUs_, a simple task-stealing queue is used, where each thread pushes and\npops from its own local redex bag, while a starving neighbor thread actively\nattempt to steal a redex from it. Since a redex is just a 64-bit value, stealing\ncan be done with a single atomic_exchange operation, making it very lightweight.\nTo reduce contention, and to force threads to steal \"old redexes\", which are\nmore likely to produce long independent workloads, this stealing is done from\nthe other end of the bag. In our experiences, this works extremely well in\npractice, achieving full CPU occupancy in all cases tested, with minimal\noverhead, and low impact on non-parallelizable programs.\n\nOn _GPUs_, this matter is more complex in many ways. First, there are two scales\non which we want sharing to occur:\n\n1. Within a running block, where stealing between local threads can be\n   accomplished by fast shared-memory operations and warp-sync primitives.\n\n2. Across global blocks, where sharing requires either a global synchronization\n   (i.e., calling the kernel again) or direct communication via global memory.\n\nUnfortunately, the cost of global synchronization (i.e., across blocks) is very\nhigh, so, having a globally shared redex bag, as in the C version, and accessing\nit within the context of a kernel, would greatly impact performance. To improve\nthis, we, initially, attempted to implement a fast block-wise scheduler, which\nsimply lets local threads pass redexes to starving ones with warp syncs:\n\n```c\n__device__ void share_redexes(TM* tm) {\n  __shared__ Pair pool[TPB];\n  Pair send, recv;\n  u32*  ini = &tm->rbag.lo_ini;\n  u32*  end = &tm->rbag.lo_end;\n  Pair* bag = tm->rbag.lo_buf;\n  for (u32 off = 1; off < 32; off *= 2) {\n    send = (*end - *ini) > 1 ? bag[*ini%RLEN] : 0;\n    recv = __shfl_xor_sync(__activemask(), send, off);\n    if (!send &&  recv) bag[((*end)++)%RLEN] = recv;\n    if ( send && !recv) ++(*ini);\n  }\n  for (u32 off = 32; off < TPB; off *= 2) {\n    u32 a = TID();\n    u32 b = a ^ off;\n    send = (*end - *ini) > 1 ? bag[*ini%RLEN] : 0;\n    pool[a] = send;\n    __syncthreads();\n    recv = pool[b];\n    if (!send &&  recv) bag[((*end)++)%RLEN] = recv;\n    if ( send && !recv) ++(*ini);\n  }\n}\n```\n\nSuch procedure is efficient enough to be called between every few interactions,\nallowing redexes to quickly fill the whole block. With that, all we had to do is\nlet the kernel perform a constant number of local interactions (usually in the\nrange of $2^9$ to $2^13$), and, once it completes, i.e., across kernel invocations,\nthe global redex bag was transposed (rows become columns), letting the entire\nGPU to fill naturally by just the block-wise sharing function above, and nothing\nelse. This approach worked very well in practice, and let us achieve a peak of\n74,000 MIPS in a shader-like functional program (i.e., a \"tree-map\" operation).\n\nUnfortunately, it didn't work so well in cases where the implied communication\nwas more involved. For example, consider the following implementation of a\npurely functional Bitonic Sort:\n\n```javascript\ndata Tree = (Leaf val) | (Node fst snd)\n\n// Swaps distant values in parallel; corresponds to a Red Box\n(warp s (Leaf a)   (Leaf b))   = (U60.swap (^ (> a b) s) (Leaf a) (Leaf b))\n(warp s (Node a b) (Node c d)) = (join (warp s a c) (warp s b d))\n\n// Rebuilds the warped tree in the original order\n(join (Node a b) (Node c d)) = (Node (Node a c) (Node b d))\n\n// Recursively warps each sub-tree; corresponds to a Blue/Green Box\n(flow s (Leaf a))   = (Leaf a)\n(flow s (Node a b)) = (down s (warp s a b))\n\n// Propagates Flow downwards\n(down s (Leaf a))   = (Leaf a)\n(down s (Node a b)) = (Node (flow s a) (flow s b))\n\n// Bitonic Sort\n(sort s (Leaf a))   = (Leaf a)\n(sort s (Node a b)) = (flow s (Node (sort 0 a) (sort 1 b)))\n```\n\nSince this is an $O(n*log(n))$ algorithm, its recursive structure unfolds in\nsuch a manner that is much less regular than a tree-map. As such, the naive task\nsharing approach had a consequence that greatly impacted performance on GPUs:\nthreads would give and receive \"misaligned\" redexes, causing warp-local threads\nto compute different calls at any given point. For example, a given warp thread\nmight be processing `(flow 5 (Node _ _))`, while another might be processing\n`(down 0 (Leaf _))` instead. This divergence has the consequence of producing\nsequentialism in the GPU architecture, where warp-local threads are in lockstep.\n\nTo improve this, a different task-sharing mechanism has been implemented, which\nrequires a minimal annotation: redexes corresponding to branching recursive\ncalls are flagged with a `!` on the global Book. With this annotation, the GPU\nevaluator will then only share redexes from functions that recurse in a\nparallelizable fashion. This is extremely effective, as it allows threads to\nalways get \"equivalent\" redexes in a regular recursive algorithm. For example,\nif given thread is processing `(flow 5 (Node _ _))`, it is very likely that\nanother warp local thread is too. This minimizes warp divergence, and has a\nprofound impact in performance across many cases. On the `bitonic_sort` example,\nthis new policy alone resulted in a jump from 1,300 MIPS to 12,000 MIPS (9x).\n\n== Optimization: Shared Memory Interactions\n\nGPUs also have another particularity that, if exploited properly, can result in\nsignificant speedups: shared memory. The NVIDIA RTX 4090, for example, includes\nA L1 Cache memory space of about 128KB, and GPU languages like CUDA usually\nallow a programmer to manually read and write from that cache, in a shared\nmemory buffer that is accessible by all threads of a block. Reading and writing\nfrom shared memory can be up to 2 orders of magnitude faster than doing so from\nglobal memory, so, using that space properly is essential to fully harness a\nGPU's computing power.\n\nOn HVM32, we use that space to store a local node buffer, and a local subst map,\nwith 8192 nodes and variables. This occupies exactly 96KB, just enough to fit\nmost modern processors. When a thread allocates a fresh node or variable, that\nallocation occurs in the shared memory, rather than the global memory. With a\nconfiguration of 128 threads per block, each thread has a \"scratchpad\" of 64\nnodes and vars to work locally, with no global memory access. This is often\nenough to compute long-running tail-loops, which is what makes HVM2 so efficient\non shader-like programs.\n\nThere is one problem, though: what happens when an interaction links a locally\nallocated node to a global variable? This would cause a pointer to a local node\nto \"leak\" to another block, which would then be unable to retrieve its\ninformation, causing a runtime error. To handle this situation, we extend the\nLINK interaction with a \"LEAK\" sub-interaction, which is specific to GPUs only.\nThat interaction essentially allocates a \"global view\" of the local node, filled\nwith two placeholder variables, such that one copy is local, and the other copy\nis global (remember: variables are always paired). That way, we can continue the\nlocal reduction without interruptions. If another block does get this \"leaked\"\nnode, it will be filled with two variables, which, in this case, act as \"future\"\nvalues which will be resolved when the local thread links it.\n\n```\n^A ~ (b1 b2)\n------------- LEAK\n^X ~ b1\n^Y ~ b2\n^A ~ ^(^X ^Y)\n```\n\nThe LEAK interaction allows us to safely work locally for as long as desired,\nwhich has great impact on performance. On the stress test benchmark, the\nthroughput jumps from about 13,000 MIPS to 54,000 MIPS by this change alone.\n\n= Garbage Collection\n\nSince HVM2 is based on Interaction Combinators, which are fully linear, there is\nno global \"garbage collection\" pass required. By IC evaluation semantics alone,\ndata is granularly allocated as needed, and freed as soon as they become\nunreachable. This is specifically accomplished by the ERAS and VOID\ninteractions, which consume sub-nets that go out of scope in parallel, clearing\nthem from memory. When HVM2 completes evaluation (i.e., after all redexes have\nbeen processed), the memory should be left with just the final result of the\nprogram, and no remaining garbage or byproduct. No further work is needed.\n\n= IO\n\nTODO: explain how we do IO\n\n= Benchmarks\n\nTODO: include some benchmarks\n\n= Translations\n\nTODO: include example translations from high-level languages to HVM2\n\n= Limitations\n\nThe HVM2 architecture, as currently presented, is capable of evaluating modern,\nhigh-level programs in massively parallel hardware with near-ideal speedup,\nwhich is a remarkable feat. That said, it has severe impactful limitations that\nmust be understood. Below is a list of many of these limitations, and how they\ncan be addressed in the future.\n\n== Only one Duplicator Node\n\nSince HVM2 is affine, duplicator nodes are often used to copy non-linear\nvariables. For example, when translating the $lambda$-term `λx.x+x`, which is not\nlinear (because `x` occurs twice), one might use a `DUP` node to clone `x`.\nDue to Interaction Net semantics, though, `DUP` nodes don't always match the\nbehavior of cloning a variable on traditional languages. This, if not handled\nproperly, can lead to *unsound reductions*. For example, the $lambda$-term:\n\n```\nC4 = (λf.λx.(f (f x)) λf.λx.(f (f x)))\n```\n\nWhich computes the Church-encoded exponentiation `2^2`, can not be soundly reduced by HVM2. To handle this, a source language must use either a type system or similar mechanism to verify that the following invariant holds:\n#v(-1em)\n#quote(block: true)[\n  _A higher-order lambda that clones its variable can not be cloned._\n]\n\nWhile restrictive, it is important to stress that this limitation only applies\nto cloning higher-order functions. A language that targets the HVM can still\nclone data types with no restrictions, and it is still able to perform loops,\nrecursion and pattern-matches with no limitations. In other words, HVM is Turing\nComplete, and can evaluate procedural languages with no restrictions, and\nfunctional languages with some restrictions. That said, this can be amended by:\n\n1. Adding more duplicator nodes. This would allow \"nested copies\" of\n   higher-order functions. With a proper type system (such as EAL inference),\n   this can greatly reduce the set of practical programs that are affected by\n   this restriction.\n\n2. Adding bookkeeping nodes. These nodes, originally proposed by Lamping (1990),\n   allow interaction systems to evaluate the full λ-calculus with no\n   restrictions. Adding bookkeeping to HVM should be easy. Sadly, this has the\n   consequence of bringing a constant-time overhead, decreasing performance by\n   about 10x. Because of that, it wasn't included in this model.\n\nIdeally, a combination of both approaches should be used: a type-checker that\nflags safe programs, which can be evaluated safely on HVM2, and a fallback\nbookkeeper, which ensures sound reductions of programs that do not. Implementing\nsuch system is outside the scope of this work, and should be done as a future\nextension. [cite: the optimizing optimal evaluation paper]\n\n== Ultra-Eager Evaluation Only\n\nIn our first implementation, HVM1, we used a lazy evaluation model. This not\nonly ensured that no unnecessary work was done, but also allowed one to compute\nwith infinite structures, like lists. Since the implementation presented here\nreduces *all* available redexes eagerly, that means neither of these hold. For\nexample, if you allocate a big structure, but only read one branch, HVM2 will\nallocate the entire structure, while HVM1 wouldn't. And if you do have an\ninfinite structure, HVM2 will never halt (because the redex bag will never be\nempty). This applies even to code that doesn't look like it is an infinite\nstructure. For example, consider the JavaScript function below:\n\n    ```\n    foo = x => x == 0 ? 0 : 1 + foo(x-1);\n    ```\n\nIn JavaScript, this is a perfectly valid function. In HVM2, if called as-is,\nthis would hang, because `foo(x-1)` would unroll infinitely, as we do not\n\"detect\" that it is in a branch. To make recursive functions computable, the\nusual approach is to split it into multiple definitions, as in:\n\n    ```\n    foo   = x => x == 0 ? fooZ : foo_S(x - 1);\n    foo_Z = 0;\n    foo_S = x => 1 + foo(x-1);\n    ```\n\nSince REFs unfold lazily, the program above will properly erase the `foo_S`\nbranch when it reaches the base case, avoiding the infinite recursion.\n\nExtending HVM2 with a full lazy mode would requires us to store uplinks,\nallowing threads to navigate through the graph and only reduce redexes that are\nreachable from the root port. While not technically hard to do, doing so would\nmake the task scheduler way more complex to implement efficiently, specially in\nthe GPU version. We reserve this for a future extension.\n\n== Single Core Inefficiency\n\nWhile HVM2 achieves near-linear speedup, allowing it to make programs run\narbitrarily faster by just using more cores (as long as there is sufficient\ndegree of parallelism), its compiler is still extremely immature, and not nearly\nas fast as state-of-art alternatives like GCC of GHC. In single-thread CPU\nevaluation, HVM2, is, baseline, still about 5x slower than GHC, and this number\ncan grow to 100x on programs that involve loops and mutable arrays, since HVM2\ndoesn't feature these yet.\n\nFor example, a single-core C program that adds numbers from 0 to a few billions\nwill easily outperform an HVM2 one that uses thousands of threads, given the C\nversion is doing no allocation, while HVM2 is allocating a tree-like recursive\nstack. That said, not every program can be implemented as an allocation-free,\nregister-mutating loop. For real programs that allocate tons of short memory\nobjects, HVM2 is expected to perform extremely well.\n\nMoreover, and unlike some might think, HVM2 is not incompatible with loops or\nmutable types, because it isn't a functional runtime, but one based on\ninteraction combinators, which are fully linear. Extending HVM2 with arrays is\nas easy as creating nodes for it, and implementing the interactions, and can be\ndone in a timely fashion as a fork of this repository. Similarly, loops can be\nimplemented by optimizing tail-calls. We plan to add such optimization soon.\n\nFinally, there are many other low-hanging fruits that could improve HVM2's\nperformance considerably. For example, currently, we do not have native\nconstructors, which means that algebraic datatypes have to be λ-encoded, which\nbrings a 2x-5x memory overhead. Adding proper constructors and eliminating this\noverhead would likely bring a proportional speedup. Similarly, adding more\nnumeric types like vectors would allow using more of the available GPU\ninstructions, and adding read-only types like immutable strings and textures\nwith 1-interaction reads would allow one to implement many algorithms that,\ncurrently, wouldn't be practical, specially for graphics rendering.\n\n== 32-bit Architecture Limitations\n\nSince this architecture is 32-bit, and since 3 bits are reserved for a tag, that\nleaves us with a 29-bit addressable space. That amounts for a total of about 500\nmillion nodes, or about 4 GB. Modern GPUs come with as much as 256 GB integrated\nmemory, so, HVM2 isn't able to fully use the available space, due to addressing\nconstraints. Moreover, its 29-bit unboxed numbers only allow for 24-bit machine\nints and floats, which may not be enough for many applications.\n\nAll these problems should be solved by extending ports to 64-bit and nodes to\n128-bits, but this requires some additional considerations, since modern GPUs\ndon't come with 128-bit atomic operations. We'll do this in a future extension.\n\n== More\n\n*Work In Progress*\n\n= Conclusion\n\nBy starting from a solid, inherently concurrent model of computation,\nInteraction Combinators, carefully designing an efficient memory format,\nimplementing lock-free concurrent interactions via lightweight atomic\nprimitives, and granularly distributing workload across all cores, we were able\nto design a parallel compiler and evaluator for high-level programming languages\nthat achieves near-linear speedup as a function of core count (within a single device).\nWhile the resulting system still has many limitations, we proposed sensible plans to\naddress them in the future.\n\nThis work creates a solid foundation for parallel programming languages that are\nable to harness the massively parallel capabilities of modern hardware, without\ndemanding explicit low-level management of threads, locks, mutexes and other\ncomplex synchronization primitives by the programmer.\n"
  },
  {
    "path": "paper/README.md",
    "content": "# HVM - Paper(s)\n\n- HVM2 (Work in progress)\n  - HVM2's theoretical foundations, implementation, early benchmarks, current limitations, and future work.\n- Extended Abstract\n  - Accepted to [FProPer 2024][1].\n  - A much shorter version of the main paper.\n\n[1]: https://icfp24.sigplan.org/home/fproper-2024\n"
  },
  {
    "path": "paper/inet.typ",
    "content": "#import \"@preview/cetz:0.2.2\": draw, canvas\n\n#let port = (name, pos, dir) => {\n  import draw: *\n  group(name: name, {\n    translate(pos)\n    rotate(dir)\n    // scale(1.5)\n    anchor(\"p\", (0, 0))\n    anchor(\"c\", (0, 0.5))\n  })\n}\n\n#let agent = (..agent) => (..args) => {\n  import draw: *\n  let style = agent.named().at(\"style\", default: ())\n  let name = args.named().at(\"name\")\n  let pos = args.named().at(\"pos\")\n  let rot = args.named().at(\"rot\", default: 0deg)\n  group(name: name, {\n    translate(pos)\n    rotate(rot)\n    translate((0, -calc.sqrt(3)/4))\n    stroke(2pt)\n    line((-.5, 0), (.5, 0), (0, calc.sqrt(3)/2), close: true, ..style, stroke: 0.5pt)\n    port(\"0\", (0, calc.sqrt(3)/2), 0deg)\n    port(\"1\", (-1/2+1/3, 0), 180deg)\n    port(\"2\", (+1/2-1/3, 0), 180deg)\n  })\n}\n\n#let link = (a, b) => {\n  import draw: *\n  stroke(2pt)\n  bezier(a + \".p\", b + \".p\", a + \".c\", b + \".c\", stroke: 0.5pt)\n}\n\n#let con = agent()\n#let dup = agent(style: (fill: black))\n"
  },
  {
    "path": "src/ast.rs",
    "content": "use TSPL::{new_parser, Parser, ParseError};\nuse highlight_error::highlight_error;\nuse crate::hvm;\nuse std::fmt::{Debug, Display};\nuse std::collections::{BTreeMap, BTreeSet};\n\n// Types\n// -----\n\n#[derive(Clone, Hash, PartialEq, Eq, Debug)]\npub struct Numb(pub u32);\n\n#[derive(Clone, Hash, PartialEq, Eq, Debug)]\npub enum Tree {\n  Var { nam: String },\n  Ref { nam: String },\n  Era,\n  Num { val: Numb },\n  Con { fst: Box<Tree>, snd: Box<Tree> },\n  Dup { fst: Box<Tree>, snd: Box<Tree> },\n  Opr { fst: Box<Tree>, snd: Box<Tree> },\n  Swi { fst: Box<Tree>, snd: Box<Tree> },\n}\n\npub type Redex = (bool, Tree, Tree);\n\n#[derive(Clone, Hash, PartialEq, Eq, Debug)]\npub struct Net {\n  pub root: Tree,\n  pub rbag: Vec<Redex>,\n}\n\npub struct Book {\n  pub defs: BTreeMap<String, Net>,\n}\n\n// Parser\n// ------\n\npub type ParseResult<T> = std::result::Result<T, ParseError>;\n\nnew_parser!(CoreParser);\n\nimpl<'i> CoreParser<'i> {\n\n  pub fn parse_numb_sym(&mut self) -> ParseResult<Numb> {\n    self.consume(\"[\")?;\n\n    // numeric casts\n    if let Some(cast) = match () {\n      _ if self.try_consume(\"u24\") => Some(hvm::TY_U24),\n      _ if self.try_consume(\"i24\") => Some(hvm::TY_I24),\n      _ if self.try_consume(\"f24\") => Some(hvm::TY_F24),\n      _ => None\n    } {\n      // Casts can't be partially applied, so nothing should follow.\n      self.consume(\"]\")?;\n\n      return Ok(Numb(hvm::Numb::new_sym(cast).0));\n    }\n\n    // Parses the symbol\n    let op = hvm::Numb::new_sym(match () {\n      // numeric operations\n      _ if self.try_consume(\"+\")   => hvm::OP_ADD,\n      _ if self.try_consume(\"-\")   => hvm::OP_SUB,\n      _ if self.try_consume(\":-\")  => hvm::FP_SUB,\n      _ if self.try_consume(\"*\")   => hvm::OP_MUL,\n      _ if self.try_consume(\"/\")   => hvm::OP_DIV,\n      _ if self.try_consume(\":/\")  => hvm::FP_DIV,\n      _ if self.try_consume(\"%\")   => hvm::OP_REM,\n      _ if self.try_consume(\":%\")  => hvm::FP_REM,\n      _ if self.try_consume(\"=\")   => hvm::OP_EQ,\n      _ if self.try_consume(\"!\")   => hvm::OP_NEQ,\n      _ if self.try_consume(\"<<\")  => hvm::OP_SHL,\n      _ if self.try_consume(\":<<\") => hvm::FP_SHL,\n      _ if self.try_consume(\">>\")  => hvm::OP_SHR,\n      _ if self.try_consume(\":>>\") => hvm::FP_SHR,\n      _ if self.try_consume(\"<\")   => hvm::OP_LT,\n      _ if self.try_consume(\">\")   => hvm::OP_GT,\n      _ if self.try_consume(\"&\")   => hvm::OP_AND,\n      _ if self.try_consume(\"|\")   => hvm::OP_OR,\n      _ if self.try_consume(\"^\")   => hvm::OP_XOR,\n      _ => self.expected(\"operator symbol\")?,\n    });\n\n    self.skip_trivia();\n    // Syntax for partial operations, like `[*2]`\n    let num = if self.peek_one() != Some(']') {\n      hvm::Numb::partial(op, hvm::Numb(self.parse_numb_lit()?.0))\n    } else {\n      op\n    };\n\n    // Closes symbol bracket\n    self.consume(\"]\")?;\n\n    // Returns the symbol\n    return Ok(Numb(num.0));\n  }\n\n  pub fn parse_numb_lit(&mut self) -> ParseResult<Numb> {\n    let ini = self.index;\n    let num = self.take_while(|x| x.is_alphanumeric() || x == '+' || x == '-' || x == '.');\n    let mut num_parser = CoreParser::new(num);\n    let end = self.index;\n    Ok(Numb(if num.contains('.') || num.contains(\"inf\") || num.contains(\"NaN\") {\n      let val: f32 = num.parse()\n        .map_err(|err| {\n          let msg = format!(\"invalid number literal: {}\\n{}\", err, highlight_error(ini, end, self.input));\n          self.expected_and::<Numb>(\"number literal\", &msg).unwrap_err()\n        })?;\n      hvm::Numb::new_f24(val)\n    } else if num.starts_with('+') || num.starts_with('-') {\n      *num_parser.index() += 1;\n      let val = num_parser.parse_u64()? as i32;\n      hvm::Numb::new_i24(if num.starts_with('-') { -val } else { val })\n    } else {\n      let val = num_parser.parse_u64()? as u32;\n      hvm::Numb::new_u24(val)\n    }.0))\n  }\n\n  pub fn parse_numb(&mut self) -> ParseResult<Numb> {\n    self.skip_trivia();\n\n    // Parses symbols (SYM)\n    if let Some('[') = self.peek_one() {\n      return self.parse_numb_sym();\n    // Parses numbers (U24,I24,F24)\n    } else {\n      return self.parse_numb_lit();\n    }\n  }\n\n  pub fn parse_tree(&mut self) -> ParseResult<Tree> {\n    self.skip_trivia();\n    //println!(\"aaa ||{}\", &self.input[self.index..]);\n    match self.peek_one() {\n      Some('(') => {\n        self.advance_one();\n        let fst = Box::new(self.parse_tree()?);\n        self.skip_trivia();\n        let snd = Box::new(self.parse_tree()?);\n        self.consume(\")\")?;\n        Ok(Tree::Con { fst, snd })\n      }\n      Some('{') => {\n        self.advance_one();\n        let fst = Box::new(self.parse_tree()?);\n        self.skip_trivia();\n        let snd = Box::new(self.parse_tree()?);\n        self.consume(\"}\")?;\n        Ok(Tree::Dup { fst, snd })\n      }\n      Some('$') => {\n        self.advance_one();\n        self.consume(\"(\")?;\n        let fst = Box::new(self.parse_tree()?);\n        self.skip_trivia();\n        let snd = Box::new(self.parse_tree()?);\n        self.consume(\")\")?;\n        Ok(Tree::Opr { fst, snd })\n      }\n      Some('?') => {\n        self.advance_one();\n        self.consume(\"(\")?;\n        let fst = Box::new(self.parse_tree()?);\n        self.skip_trivia();\n        let snd = Box::new(self.parse_tree()?);\n        self.consume(\")\")?;\n        Ok(Tree::Swi { fst, snd })\n      }\n      Some('@') => {\n        self.advance_one();\n        let nam = self.parse_name()?;\n        Ok(Tree::Ref { nam })\n      }\n      Some('*') => {\n        self.advance_one();\n        Ok(Tree::Era)\n      }\n      _ => {\n        if let Some(c) = self.peek_one() {\n          if \"0123456789+-[\".contains(c) {\n            return Ok(Tree::Num { val: self.parse_numb()? });\n          }\n        }\n        let nam = self.parse_name()?;\n        Ok(Tree::Var { nam })\n      }\n    }\n  }\n\n  pub fn parse_net(&mut self) -> ParseResult<Net> {\n    let root = self.parse_tree()?;\n    let mut rbag = Vec::new();\n    self.skip_trivia();\n    while self.peek_one() == Some('&') {\n      self.consume(\"&\")?;\n      let par = if let Some('!') = self.peek_one() { self.consume(\"!\")?; true } else { false };\n      let fst = self.parse_tree()?;\n      self.consume(\"~\")?;\n      let snd = self.parse_tree()?;\n      rbag.push((par,fst,snd));\n      self.skip_trivia();\n    }\n    Ok(Net { root, rbag })\n  }\n\n  pub fn parse_book(&mut self) -> ParseResult<Book> {\n    let mut defs = BTreeMap::new();\n    while !self.is_eof() {\n      self.consume(\"@\")?;\n      let name = self.parse_name()?;\n      self.consume(\"=\")?;\n      let net = self.parse_net()?;\n      defs.insert(name, net);\n    }\n    Ok(Book { defs })\n  }\n\n  fn try_consume(&mut self, str: &str) -> bool {\n    let matches = self.peek_many(str.len()) == Some(str);\n    if matches {\n      self.advance_many(str.len());\n    }\n    matches\n  }\n}\n\n// Stringifier\n// -----------\n\nimpl Numb {\n  pub fn show(&self) -> String {\n    let numb = hvm::Numb(self.0);\n    match numb.get_typ() {\n      hvm::TY_SYM => match numb.get_sym() as hvm::Tag {\n        // casts\n        hvm::TY_U24 => \"[u24]\".to_string(),\n        hvm::TY_I24 => \"[i24]\".to_string(),\n        hvm::TY_F24 => \"[f24]\".to_string(),\n        // operations\n        hvm::OP_ADD => \"[+]\".to_string(),\n        hvm::OP_SUB => \"[-]\".to_string(),\n        hvm::FP_SUB => \"[:-]\".to_string(),\n        hvm::OP_MUL => \"[*]\".to_string(),\n        hvm::OP_DIV => \"[/]\".to_string(),\n        hvm::FP_DIV => \"[:/]\".to_string(),\n        hvm::OP_REM => \"[%]\".to_string(),\n        hvm::FP_REM => \"[:%]\".to_string(),\n        hvm::OP_EQ  => \"[=]\".to_string(),\n        hvm::OP_NEQ => \"[!]\".to_string(),\n        hvm::OP_LT  => \"[<]\".to_string(),\n        hvm::OP_GT  => \"[>]\".to_string(),\n        hvm::OP_AND => \"[&]\".to_string(),\n        hvm::OP_OR  => \"[|]\".to_string(),\n        hvm::OP_XOR => \"[^]\".to_string(),\n        hvm::OP_SHL => \"[<<]\".to_string(),\n        hvm::FP_SHL => \"[:<<]\".to_string(),\n        hvm::OP_SHR => \"[>>]\".to_string(),\n        hvm::FP_SHR => \"[:>>]\".to_string(),\n        _           => \"[?]\".to_string(),\n      }\n      hvm::TY_U24 => {\n        let val = numb.get_u24();\n        format!(\"{}\", val)\n      }\n      hvm::TY_I24 => {\n        let val = numb.get_i24();\n        format!(\"{:+}\", val)\n      }\n      hvm::TY_F24 => {\n        let val = numb.get_f24();\n        if val.is_infinite() {\n          if val.is_sign_positive() {\n            format!(\"+inf\")\n          } else {\n            format!(\"-inf\")\n          }\n        } else if val.is_nan() {\n          format!(\"+NaN\")\n        } else {\n          format!(\"{:?}\", val)\n        }\n      }\n      _ => {\n        let typ = numb.get_typ();\n        let val = numb.get_u24();\n        format!(\"[{}0x{:07X}]\", match typ {\n          hvm::OP_ADD => \"+\",\n          hvm::OP_SUB => \"-\",\n          hvm::FP_SUB => \":-\",\n          hvm::OP_MUL => \"*\",\n          hvm::OP_DIV => \"/\",\n          hvm::FP_DIV => \":/\",\n          hvm::OP_REM => \"%\",\n          hvm::FP_REM => \":%\",\n          hvm::OP_EQ  => \"=\",\n          hvm::OP_NEQ => \"!\",\n          hvm::OP_LT  => \"<\",\n          hvm::OP_GT  => \">\",\n          hvm::OP_AND => \"&\",\n          hvm::OP_OR  => \"|\",\n          hvm::OP_XOR => \"^\",\n          hvm::OP_SHL => \"<<\",\n          hvm::FP_SHL => \":<<\",\n          hvm::OP_SHR => \">>\",\n          hvm::FP_SHR => \":>>\",\n          _           => \"?\",\n        }, val)\n      }\n    }\n  }\n}\n\nimpl Tree {\n  pub fn show(&self) -> String {\n    match self {\n      Tree::Var { nam } => nam.to_string(),\n      Tree::Ref { nam } => format!(\"@{}\", nam),\n      Tree::Era => \"*\".to_string(),\n      Tree::Num { val } => format!(\"{}\", val.show()),\n      Tree::Con { fst, snd } => format!(\"({} {})\", fst.show(), snd.show()),\n      Tree::Dup { fst, snd } => format!(\"{{{} {}}}\", fst.show(), snd.show()),\n      Tree::Opr { fst, snd } => format!(\"$({} {})\", fst.show(), snd.show()),\n      Tree::Swi { fst, snd } => format!(\"?({} {})\", fst.show(), snd.show()),\n    }\n  }\n}\n\nimpl Net {\n  pub fn show(&self) -> String {\n    let mut s = self.root.show();\n    for (par, fst, snd) in &self.rbag {\n      s.push_str(\" &\");\n      s.push_str(if *par { \"!\" } else { \" \" });\n      s.push_str(&fst.show());\n      s.push_str(\" ~ \");\n      s.push_str(&snd.show());\n    }\n    s\n  }\n}\n\nimpl Book {\n  pub fn show(&self) -> String {\n    let mut s = String::new();\n    for (name, net) in &self.defs {\n      s.push_str(\"@\");\n      s.push_str(name);\n      s.push_str(\" = \");\n      s.push_str(&net.show());\n      s.push('\\n');\n    }\n    s\n  }\n}\n\n// Readback\n// --------\n\nimpl Tree {\n  pub fn readback(net: &hvm::GNet, port: hvm::Port, fids: &BTreeMap<hvm::Val, String>) -> Option<Tree> {\n    //println!(\"reading {}\", port.show());\n    match port.get_tag() {\n      hvm::VAR => {\n        let got = net.enter(port);\n        if got != port {\n          return Tree::readback(net, got, fids);\n        } else {\n          return Some(Tree::Var { nam: format!(\"v{:x}\", port.get_val()) });\n        }\n      }\n      hvm::REF => {\n        return Some(Tree::Ref { nam: fids.get(&port.get_val())?.clone() });\n      }\n      hvm::ERA => {\n        return Some(Tree::Era);\n      }\n      hvm::NUM => {\n        return Some(Tree::Num { val: Numb(port.get_val()) });\n      }\n      hvm::CON => {\n        let pair = net.node_load(port.get_val() as usize);\n        let fst = Tree::readback(net, pair.get_fst(), fids)?;\n        let snd = Tree::readback(net, pair.get_snd(), fids)?;\n        return Some(Tree::Con { fst: Box::new(fst), snd: Box::new(snd) });\n      }\n      hvm::DUP => {\n        let pair = net.node_load(port.get_val() as usize);\n        let fst = Tree::readback(net, pair.get_fst(), fids)?;\n        let snd = Tree::readback(net, pair.get_snd(), fids)?;\n        return Some(Tree::Dup { fst: Box::new(fst), snd: Box::new(snd) });\n      }\n      hvm::OPR => {\n        let pair = net.node_load(port.get_val() as usize);\n        let fst = Tree::readback(net, pair.get_fst(), fids)?;\n        let snd = Tree::readback(net, pair.get_snd(), fids)?;\n        return Some(Tree::Opr { fst: Box::new(fst), snd: Box::new(snd) });\n      }\n      hvm::SWI => {\n        let pair = net.node_load(port.get_val() as usize);\n        let fst = Tree::readback(net, pair.get_fst(), fids)?;\n        let snd = Tree::readback(net, pair.get_snd(), fids)?;\n        return Some(Tree::Swi { fst: Box::new(fst), snd: Box::new(snd) });\n      }\n      _ => {\n        unreachable!()\n      }\n    }\n  }\n}\n\nimpl Net {\n  pub fn readback(net: &hvm::GNet, book: &hvm::Book) -> Option<Net> {\n    let mut fids = BTreeMap::new();\n    for (fid, def) in book.defs.iter().enumerate() {\n      fids.insert(fid as hvm::Val, def.name.clone());\n    }\n    let root = net.enter(hvm::ROOT);\n    let root = Tree::readback(net, root, &fids)?;\n    let rbag = Vec::new();\n    return Some(Net { root, rbag });\n  }\n}\n\n// Def Builder\n// -----------\n\nimpl Tree {\n  pub fn build(&self, def: &mut hvm::Def, fids: &BTreeMap<String, hvm::Val>, vars: &mut BTreeMap<String, hvm::Val>) -> hvm::Port {\n    match self {\n      Tree::Var { nam } => {\n        if !vars.contains_key(nam) {\n          vars.insert(nam.clone(), vars.len() as hvm::Val);\n          def.vars += 1;\n        }\n        return hvm::Port::new(hvm::VAR, *vars.get(nam).unwrap());\n      }\n      Tree::Ref { nam } => {\n        if let Some(fid) = fids.get(nam) {\n          return hvm::Port::new(hvm::REF, *fid);\n        } else {\n          panic!(\"Unbound definition: {}\", nam);\n        }\n      }\n      Tree::Era => {\n        return hvm::Port::new(hvm::ERA, 0);\n      }\n      Tree::Num { val } => {\n        return hvm::Port::new(hvm::NUM, val.0);\n      }\n      Tree::Con { fst, snd } => {\n        let index = def.node.len();\n        def.node.push(hvm::Pair(0));\n        let p1 = fst.build(def, fids, vars);\n        let p2 = snd.build(def, fids, vars);\n        def.node[index] = hvm::Pair::new(p1, p2);\n        return hvm::Port::new(hvm::CON, index as hvm::Val);\n      }\n      Tree::Dup { fst, snd } => {\n        def.safe = false;\n        let index = def.node.len();\n        def.node.push(hvm::Pair(0));\n        let p1 = fst.build(def, fids, vars);\n        let p2 = snd.build(def, fids, vars);\n        def.node[index] = hvm::Pair::new(p1, p2);\n        return hvm::Port::new(hvm::DUP, index as hvm::Val);\n      },\n      Tree::Opr { fst, snd } => {\n        let index = def.node.len();\n        def.node.push(hvm::Pair(0));\n        let p1 = fst.build(def, fids, vars);\n        let p2 = snd.build(def, fids, vars);\n        def.node[index] = hvm::Pair::new(p1, p2);\n        return hvm::Port::new(hvm::OPR, index as hvm::Val);\n      },\n      Tree::Swi { fst, snd } => {\n        let index = def.node.len();\n        def.node.push(hvm::Pair(0));\n        let p1 = fst.build(def, fids, vars);\n        let p2 = snd.build(def, fids, vars);\n        def.node[index] = hvm::Pair::new(p1, p2);\n        return hvm::Port::new(hvm::SWI, index as hvm::Val);\n      },\n    }\n  }\n\n  pub fn direct_dependencies<'name>(&'name self) -> BTreeSet<&'name str> {\n    let mut stack: Vec<&Tree> = vec![self];\n    let mut acc: BTreeSet<&'name str> = BTreeSet::new();\n    \n    while let Some(curr) = stack.pop() {\n      match curr {\n        Tree::Ref { nam } => { acc.insert(nam); },\n        Tree::Con { fst, snd } => { stack.push(fst); stack.push(snd); },\n        Tree::Dup { fst, snd } => { stack.push(fst); stack.push(snd); },\n        Tree::Opr { fst, snd } => { stack.push(fst); stack.push(snd); },\n        Tree::Swi { fst, snd } => { stack.push(fst); stack.push(snd); },\n        Tree::Num { val } => {},\n        Tree::Var { nam } => {},\n        Tree::Era => {},\n      };\n    }\n    acc\n  }\n}\n\nimpl Net {\n  pub fn build(&self, def: &mut hvm::Def, fids: &BTreeMap<String, hvm::Val>, vars: &mut BTreeMap<String, hvm::Val>) {\n    let index = def.node.len();\n    def.root = self.root.build(def, fids, vars);\n    for (par, fst, snd) in &self.rbag {\n      let index = def.rbag.len();\n      def.rbag.push(hvm::Pair(0));\n      let p1 = fst.build(def, fids, vars);\n      let p2 = snd.build(def, fids, vars);\n      let rx = hvm::Pair::new(p1, p2);\n      let rx = if *par { rx.set_par_flag() } else { rx };\n      def.rbag[index] = rx;\n    }\n  }\n}\n\nimpl Book {\n  pub fn parse(code: &str) -> ParseResult<Self> {\n    CoreParser::new(code).parse_book()\n  }\n\n  pub fn build(&self) -> hvm::Book {\n    let mut name_to_fid = BTreeMap::new();\n    let mut fid_to_name = BTreeMap::new();\n    fid_to_name.insert(0, \"main\".to_string());\n    name_to_fid.insert(\"main\".to_string(), 0);\n    for (_i, (name, _)) in self.defs.iter().enumerate() {\n      if name != \"main\" {\n        fid_to_name.insert(name_to_fid.len() as hvm::Val, name.clone());\n        name_to_fid.insert(name.clone(), name_to_fid.len() as hvm::Val);\n      }\n    }\n    let mut book = hvm::Book { defs: Vec::new() };\n    for (fid, name) in &fid_to_name {\n      let ast_def = self.defs.get(name).expect(\"missing `@main` definition\");\n      let mut def = hvm::Def {\n        name: name.clone(),\n        safe: true,\n        root: hvm::Port(0),\n        rbag: vec![],\n        node: vec![],\n        vars: 0,\n      };\n      ast_def.build(&mut def, &name_to_fid, &mut BTreeMap::new());\n      book.defs.push(def);\n    }\n    self.propagate_safety(&mut book, &name_to_fid);\n    return book;\n  }\n\n  /// Propagate unsafe definitions to those that reference them.\n  ///\n  /// When calling this function, it is expected that definitions that are directly\n  /// unsafe are already marked as such in the `compiled_book`.\n  /// \n  /// This does not completely solve the cloning safety in HVM. It only stops invalid\n  /// **global** definitions from being cloned, but local unsafe code can still be\n  /// cloned and can generate seemingly unexpected results, such as placing eraser\n  /// nodes in weird places. See HVM issue [#362](https://github.com/HigherOrderCO/HVM/issues/362)\n  /// for an example.\n  fn propagate_safety(&self, compiled_book: &mut hvm::Book, lookup: &BTreeMap<String, u32>) {\n    let dependents = self.direct_dependents();\n    let mut stack: Vec<&str> = Vec::new();\n\n    for (name, _) in self.defs.iter() {\n      let def = &mut compiled_book.defs[lookup[name] as usize];\n      if !def.safe {\n        for next in dependents[name.as_str()].iter() {\n          stack.push(next);\n        }\n      }\n    }\n\n    while let Some(curr) = stack.pop() {\n      let def = &mut compiled_book.defs[lookup[curr] as usize];\n      if !def.safe {\n        // Already visited, skip this\n        continue;\n      }\n\n      def.safe = false;\n\n      for &next in dependents[curr].iter() {\n        stack.push(next);\n      }\n    }\n  }\n\n  /// Calculates the dependents of each definition, that is, if definition `A`\n  /// requires `B`, `B: A` is in the return map. This is used to propagate unsafe\n  /// definitions to others that depend on them.\n  /// \n  /// This solution has linear complexity on the number of definitions in the\n  /// book and the number of direct references in each definition, but it also\n  /// traverses each definition's trees entirely once.\n  ///\n  /// Complexity: O(d*t + r)\n  /// - `d` is the number of definitions in the book\n  /// - `r` is the number of direct references in each definition\n  /// - `t` is the number of nodes in each tree\n  fn direct_dependents<'name>(&'name self) -> BTreeMap<&'name str, BTreeSet<&'name str>> {\n    let mut result = BTreeMap::new();\n    for (name, _) in self.defs.iter() {\n      result.insert(name.as_str(), BTreeSet::new());\n    }\n\n    let mut process = |tree: &'name Tree, name: &'name str| {\n      for dependency in tree.direct_dependencies() {\n        result\n          .get_mut(dependency)\n          .expect(\"global definition depends on undeclared reference\")\n          .insert(name);\n      }\n    };\n\n    for (name, net) in self.defs.iter() {\n      process(&net.root, name);\n      for (_, r1, r2) in net.rbag.iter() {\n        process(r1, name);\n        process(r2, name);\n      }\n    }\n    result\n  }\n}\n"
  },
  {
    "path": "src/cmp.rs",
    "content": "use crate::ast;\nuse crate::hvm;\n\nuse std::collections::HashMap;\n\n#[derive(Clone, Copy, PartialEq, Eq)]\npub enum Target { CUDA, C }\n\n// Compiles a whole Book.\npub fn compile_book(trg: Target, book: &hvm::Book) -> String {\n  let mut code = String::new();\n\n  // Compiles functions\n  for fid in 0..book.defs.len() {\n    compile_def(trg, &mut code, book, 0, fid as hvm::Val);\n    code.push_str(&format!(\"\\n\"));\n  }\n\n  // Compiles interact_call\n  if trg == Target::CUDA {\n    code.push_str(&format!(\"__device__ \"));\n  }\n  code.push_str(&format!(\"bool interact_call(Net *net, TM *tm, Port a, Port b) {{\\n\"));\n  code.push_str(&format!(\"  u32 fid = get_val(a) & 0xFFFFFFF;\\n\"));\n  code.push_str(&format!(\"  switch (fid) {{\\n\"));\n  for (fid, def) in book.defs.iter().enumerate() {\n    code.push_str(&format!(\"    case {}: return interact_call_{}(net, tm, a, b);\\n\", fid, &def.name.replace(\"/\",\"_\").replace(\".\",\"_\").replace(\"-\",\"_\")));\n  }\n  code.push_str(&format!(\"    default: return false;\\n\"));\n  code.push_str(&format!(\"  }}\\n\"));\n  code.push_str(&format!(\"}}\"));\n\n  return code;\n}\n\n// Compiles a single Def.\npub fn compile_def(trg: Target, code: &mut String, book: &hvm::Book, tab: usize, fid: hvm::Val) {\n  let def = &book.defs[fid as usize];\n  let fun = &def.name.replace(\"/\",\"_\").replace(\".\",\"_\").replace(\"-\",\"_\");\n\n  // Initializes context\n  let neo = &mut 0;\n  \n  // Generates function\n  if trg == Target::CUDA {\n    code.push_str(&format!(\"__device__ \"));\n  }\n  code.push_str(&format!(\"{}bool interact_call_{}(Net *net, TM *tm, Port a, Port b) {{\\n\", indent(tab), fun));\n  // Fast DUP-REF\n  if def.safe {\n    code.push_str(&format!(\"{}if (get_tag(b) == DUP) {{\\n\", indent(tab+1)));\n    code.push_str(&format!(\"{}return interact_eras(net, tm, a, b);\\n\", indent(tab+2)));\n    code.push_str(&format!(\"{}}}\\n\", indent(tab+1)));\n  }\n  code.push_str(&format!(\"{}u32 vl = 0;\\n\", indent(tab+1)));\n  code.push_str(&format!(\"{}u32 nl = 0;\\n\", indent(tab+1)));\n\n  // Allocs resources (using fast allocator)\n  for i in 0 .. def.vars {\n    code.push_str(&format!(\"{}Val v{:x} = vars_alloc_1(net, tm, &vl);\\n\", indent(tab+1), i));\n  }\n  for i in 0 .. def.node.len() {\n    code.push_str(&format!(\"{}Val n{:x} = node_alloc_1(net, tm, &nl);\\n\", indent(tab+1), i));\n  }\n  code.push_str(&format!(\"{}if (0\", indent(tab+1)));\n  for i in 0 .. def.vars {\n    code.push_str(&format!(\" || !v{:x}\", i));\n  }\n  for i in 0 .. def.node.len() {\n    code.push_str(&format!(\" || !n{:x}\", i));\n  }\n  code.push_str(&format!(\") {{\\n\"));\n  code.push_str(&format!(\"{}return false;\\n\", indent(tab+2)));\n  code.push_str(&format!(\"{}}}\\n\", indent(tab+1)));\n  for i in 0 .. def.vars {\n    code.push_str(&format!(\"{}vars_create(net, v{:x}, NONE);\\n\", indent(tab+1), i));\n  }\n\n  // Allocs resources (using slow allocator)\n  //code.push_str(&format!(\"{}// Allocates needed resources.\\n\", indent(tab+1)));\n  //code.push_str(&format!(\"{}if (!get_resources(net, tm, {}, {}, {})) {{\\n\", indent(tab+1), def.rbag.len()+1, def.node.len(), def.vars));\n  //code.push_str(&format!(\"{}return false;\\n\", indent(tab+2)));\n  //code.push_str(&format!(\"{}}}\\n\", indent(tab+1)));\n  //for i in 0 .. def.node.len() {\n    //code.push_str(&format!(\"{}Val n{:x} = tm->nloc[0x{:x}];\\n\", indent(tab+1), i, i));\n  //}\n  //for i in 0 .. def.vars {\n    //code.push_str(&format!(\"{}Val v{:x} = tm->vloc[0x{:x}];\\n\", indent(tab+1), i, i));\n  //}\n  //for i in 0 .. def.vars {\n    //code.push_str(&format!(\"{}vars_create(net, v{:x}, NONE);\\n\", indent(tab+1), i));\n  //}\n\n  // Compiles root\n  compile_link_fast(trg, code, book, neo, tab+1, def, def.root, \"b\");\n\n  // Compiles rbag\n  for redex in &def.rbag {\n    let fun = compile_node(trg, code, book, neo, tab+1, def, redex.get_fst());\n    let arg = compile_node(trg, code, book, neo, tab+1, def, redex.get_snd());\n    code.push_str(&format!(\"{}link(net, tm, {}, {});\\n\", indent(tab+1), &fun, &arg));\n  }\n\n  // Return\n  code.push_str(&format!(\"{}return true;\\n\", indent(tab+1)));\n  code.push_str(&format!(\"{}}}\\n\", indent(tab)));\n}\n\n// Compiles a link, performing some pre-defined static reductions.\npub fn compile_link_fast(trg: Target, code: &mut String, book: &hvm::Book, neo: &mut usize, tab: usize, def: &hvm::Def, a: hvm::Port, b: &str) {\n\n  // (<?(a111 a112) a12> a2) <~ (#X R)\n  // --------------------------------- fast SWITCH\n  // if X == 0:\n  //   a111 <~ R\n  //   a112 <~ ERAS\n  // else:\n  //   a111 <~ ERAS\n  //   a112 <~ (#(X-1) R)\n  if trg != Target::CUDA && a.get_tag() == hvm::CON {\n    let a_ = &def.node[a.get_val() as usize];\n    let a1 = a_.get_fst();\n    let a2 = a_.get_snd();\n    if a1.get_tag() == hvm::SWI {\n      let a1_ = &def.node[a1.get_val() as usize];\n      let a11 = a1_.get_fst();\n      let a12 = a1_.get_snd();\n      if a11.get_tag() == hvm::CON && a2.get_tag() == hvm::VAR && a12.0 == a2.0 {\n        let a11_ = &def.node[a11.get_val() as usize];\n        let a111 = a11_.get_fst();\n        let a112 = a11_.get_snd();\n        let op   = fresh(neo);\n        let bv   = fresh(neo);\n        let x1   = fresh(neo);\n        let x2   = fresh(neo);\n        let nu   = fresh(neo);\n        code.push_str(&format!(\"{}bool {} = 0;\\n\", indent(tab), &op));\n        code.push_str(&format!(\"{}Pair {} = 0;\\n\", indent(tab), &bv));\n        code.push_str(&format!(\"{}Port {} = NONE;\\n\", indent(tab), &nu));\n        code.push_str(&format!(\"{}Port {} = NONE;\\n\", indent(tab), &x1));\n        code.push_str(&format!(\"{}Port {} = NONE;\\n\", indent(tab), &x2));\n        code.push_str(&format!(\"{}//fast switch\\n\", indent(tab)));\n        code.push_str(&format!(\"{}if (get_tag({}) == CON) {{\\n\", indent(tab), b));\n        code.push_str(&format!(\"{}{} = node_load(net, get_val({}));\\n\", indent(tab+1), &bv, b)); // recycled\n        code.push_str(&format!(\"{}{} = enter(net,get_fst({}));\\n\", indent(tab+1), &nu, &bv));\n        code.push_str(&format!(\"{}if (get_tag({}) == NUM) {{\\n\", indent(tab+1), &nu));\n        code.push_str(&format!(\"{}tm->itrs += 3;\\n\", indent(tab+2)));\n        code.push_str(&format!(\"{}vars_take(net, v{});\\n\", indent(tab+2), a2.get_val()));\n        code.push_str(&format!(\"{}{} = 1;\\n\", indent(tab+2), &op));\n        code.push_str(&format!(\"{}if (get_u24(get_val({})) == 0) {{\\n\", indent(tab+2), &nu));\n        code.push_str(&format!(\"{}node_take(net, get_val({}));\\n\", indent(tab+3), b));\n        code.push_str(&format!(\"{}{} = get_snd({});\\n\", indent(tab+3), &x1, &bv));\n        code.push_str(&format!(\"{}{} = new_port(ERA,0);\\n\", indent(tab+3), &x2));\n        code.push_str(&format!(\"{}}} else {{\\n\", indent(tab+2)));\n        code.push_str(&format!(\"{}node_store(net, get_val({}), new_pair(new_port(NUM,new_u24(get_u24(get_val({}))-1)), get_snd({})));\\n\", indent(tab+3), b, &nu, &bv));\n        code.push_str(&format!(\"{}{} = new_port(ERA,0);\\n\", indent(tab+3), &x1));\n        code.push_str(&format!(\"{}{} = {};\\n\", indent(tab+3), &x2, b));\n        code.push_str(&format!(\"{}}}\\n\", indent(tab+2)));\n        code.push_str(&format!(\"{}}} else {{\\n\", indent(tab+1)));\n        code.push_str(&format!(\"{}node_store(net, get_val({}), new_pair({},get_snd({})));\\n\", indent(tab+2), b, &nu, &bv)); // update \"entered\" var\n        code.push_str(&format!(\"{}}}\\n\", indent(tab+1)));\n        code.push_str(&format!(\"{}}}\\n\", indent(tab+0)));\n        compile_link_fast(trg, code, book, neo, tab, def, a111, &x1);\n        compile_link_fast(trg, code, book, neo, tab, def, a112, &x2);\n        code.push_str(&format!(\"{}if (!{}) {{\\n\", indent(tab), &op));\n        code.push_str(&format!(\"{}node_create(net, n{:x}, new_pair(new_port(SWI,n{}),new_port(VAR,v{})));\\n\", indent(tab+1), a.get_val(), a1.get_val(), a2.get_val()));\n        code.push_str(&format!(\"{}node_create(net, n{:x}, new_pair(new_port(CON,n{}),new_port(VAR,v{})));\\n\", indent(tab+1), a1.get_val(), a11.get_val(), a12.get_val()));\n        code.push_str(&format!(\"{}node_create(net, n{:x}, new_pair({},{}));\\n\", indent(tab+1), a11.get_val(), &x1, &x2));\n        link_or_store(trg, code, book, neo, tab+1, def, &format!(\"new_port(CON, n{:x})\", a.get_val()), b);\n        code.push_str(&format!(\"{}}}\\n\", indent(tab)));\n        return;\n      }\n    }\n  }\n\n  // FIXME: REVIEW\n  // <+ #B r> <~ #A\n  // --------------- fast OPER\n  // r <~ #(op(A,B))\n  if trg != Target::CUDA && a.get_tag() == hvm::OPR {\n    let a_ = &def.node[a.get_val() as usize];\n    let a1 = a_.get_fst();\n    let a2 = a_.get_snd();\n    let op = fresh(neo);\n    let x1 = compile_node(trg, code, book, neo, tab, def, a1);\n    let x2 = fresh(neo);\n    code.push_str(&format!(\"{}bool {} = 0;\\n\", indent(tab), &op));\n    code.push_str(&format!(\"{}Port {} = NONE;\\n\", indent(tab), &x2));\n    code.push_str(&format!(\"{}// fast oper\\n\", indent(tab)));\n    code.push_str(&format!(\"{}if (get_tag({}) == NUM && get_tag({}) == NUM) {{\\n\", indent(tab), b, &x1));\n    code.push_str(&format!(\"{}tm->itrs += 1;\\n\", indent(tab+1)));\n    code.push_str(&format!(\"{}{} = 1;\\n\", indent(tab+1), &op));\n    code.push_str(&format!(\"{}{} = new_port(NUM, operate(get_val({}), get_val({})));\\n\", indent(tab+1), &x2, b, &x1));\n    code.push_str(&format!(\"{}}}\\n\", indent(tab)));\n    compile_link_fast(trg, code, book, neo, tab, def, a2, &x2);\n    code.push_str(&format!(\"{}if (!{}) {{\\n\", indent(tab), &op));\n    code.push_str(&format!(\"{}node_create(net, n{:x}, new_pair({},{}));\\n\", indent(tab+1), a.get_val(), &x1, &x2));\n    link_or_store(trg, code, book, neo, tab+1, def, &format!(\"new_port(OPR, n{:x})\", a.get_val()), b);\n    code.push_str(&format!(\"{}}}\\n\", indent(tab)));\n    return;\n  }\n\n  // FIXME: REVIEW\n  // {a1 a2} <~ #v\n  // ------------- Fast COPY\n  // a1 <~ #v\n  // a2 <~ #v\n  if trg != Target::CUDA && a.get_tag() == hvm::DUP {\n    let a_ = &def.node[a.get_val() as usize];\n    let p1 = a_.get_fst();\n    let p2 = a_.get_snd();\n    let op = fresh(neo);\n    let x1 = fresh(neo);\n    let x2 = fresh(neo);\n    code.push_str(&format!(\"{}bool {} = 0;\\n\", indent(tab), &op));\n    code.push_str(&format!(\"{}Port {} = NONE;\\n\", indent(tab), &x1));\n    code.push_str(&format!(\"{}Port {} = NONE;\\n\", indent(tab), &x2));\n    code.push_str(&format!(\"{}// fast copy\\n\", indent(tab)));\n    code.push_str(&format!(\"{}if (get_tag({}) == NUM) {{\\n\", indent(tab), b));\n    code.push_str(&format!(\"{}tm->itrs += 1;\\n\", indent(tab+1)));\n    code.push_str(&format!(\"{}{} = 1;\\n\", indent(tab+1), &op));\n    code.push_str(&format!(\"{}{} = {};\\n\", indent(tab+1), &x1, b));\n    code.push_str(&format!(\"{}{} = {};\\n\", indent(tab+1), &x2, b));\n    code.push_str(&format!(\"{}}}\\n\", indent(tab)));\n    compile_link_fast(trg, code, book, neo, tab, def, p2, &x2);\n    compile_link_fast(trg, code, book, neo, tab, def, p1, &x1);\n    code.push_str(&format!(\"{}if (!{}) {{\\n\", indent(tab), &op));\n    code.push_str(&format!(\"{}node_create(net, n{:x}, new_pair({},{}));\\n\", indent(tab+1), a.get_val(), x1, x2));\n    link_or_store(trg, code, book, neo, tab+1, def, &format!(\"new_port(DUP,n{:x})\", a.get_val()), b);\n    code.push_str(&format!(\"{}}}\\n\", indent(tab)));\n    return;\n  }\n\n  // (a1 a2) <~ (x1 x2)\n  // ------------------ Fast ANNI\n  // a1 <~ x1\n  // a2 <~ x2\n  if trg != Target::CUDA && a.get_tag() == hvm::CON {\n    let a_ = &def.node[a.get_val() as usize];\n    let a1 = a_.get_fst();\n    let a2 = a_.get_snd();\n    let op = fresh(neo);\n    let bv = fresh(neo);\n    let x1 = fresh(neo);\n    let x2 = fresh(neo);\n    code.push_str(&format!(\"{}bool {} = 0;\\n\", indent(tab), &op));\n    code.push_str(&format!(\"{}Pair {} = 0;\\n\", indent(tab), &bv));\n    code.push_str(&format!(\"{}Port {} = NONE;\\n\", indent(tab), &x1));\n    code.push_str(&format!(\"{}Port {} = NONE;\\n\", indent(tab), &x2));\n    code.push_str(&format!(\"{}// fast anni\\n\", indent(tab)));\n    code.push_str(&format!(\"{}if (get_tag({}) == CON && node_load(net, get_val({})) != 0) {{\\n\", indent(tab), b, b));\n    //code.push_str(&format!(\"{}atomic_fetch_add(&FAST, 1);\\n\", indent(tab+1)));\n    code.push_str(&format!(\"{}tm->itrs += 1;\\n\", indent(tab+1)));\n    code.push_str(&format!(\"{}{} = 1;\\n\", indent(tab+1), &op));\n    code.push_str(&format!(\"{}{} = node_take(net, get_val({}));\\n\", indent(tab+1), &bv, b));\n    code.push_str(&format!(\"{}{} = get_fst({});\\n\", indent(tab+1), x1, &bv));\n    code.push_str(&format!(\"{}{} = get_snd({});\\n\", indent(tab+1), x2, &bv));\n    code.push_str(&format!(\"{}}}\\n\", indent(tab)));\n    //code.push_str(&format!(\"{}else {{ atomic_fetch_add(&SLOW, 1); }}\\n\", indent(tab)));\n    compile_link_fast(trg, code, book, neo, tab, def, a2, &x2);\n    compile_link_fast(trg, code, book, neo, tab, def, a1, &x1);\n    code.push_str(&format!(\"{}if (!{}) {{\\n\", indent(tab), &op));\n    code.push_str(&format!(\"{}node_create(net, n{:x}, new_pair({},{}));\\n\", indent(tab+1), a.get_val(), x1, x2));\n    link_or_store(trg, code, book, neo, tab+1, def, &format!(\"new_port(CON,n{:x})\", a.get_val()), b);\n    code.push_str(&format!(\"{}}}\\n\", indent(tab)));\n    return;\n  }\n\n  // FIXME: since get_tag(NONE) == REF, comparing if something's tag is REF always has the issue of\n  // returning true when that thing is NONE. this caused a bug in the optimization below. in\n  // general, this is a potential source of bugs across the entire implementation, so we always\n  // need to check that case. an alternative, of course, would be to make get_tag handle this, but\n  // I'm concerned about the performance issues. so, instead, we should make sure that, across the\n  // entire codebase, we never use get_tag expecting a REF on something that might be NONE\n  \n  // ATOM <~ *\n  // --------- Fast VOID\n  // nothing\n  if trg != Target::CUDA && (a.get_tag() == hvm::NUM || a.get_tag() == hvm::ERA) {\n    code.push_str(&format!(\"{}// fast void\\n\", indent(tab)));\n    code.push_str(&format!(\"{}if (get_tag({}) == ERA || get_tag({}) == NUM) {{\\n\", indent(tab), b, b));\n    code.push_str(&format!(\"{}tm->itrs += 1;\\n\", indent(tab+1)));\n    code.push_str(&format!(\"{}}} else {{\\n\", indent(tab)));\n    compile_link_slow(trg, code, book, neo, tab+1, def, a, b);\n    code.push_str(&format!(\"{}}}\\n\", indent(tab)));\n    return;\n  }\n\n  compile_link_slow(trg, code, book, neo, tab, def, a, b);\n}\n\n// Compiles a link, without pre-defined reductions.\npub fn compile_link_slow(trg: Target, code: &mut String, book: &hvm::Book, neo: &mut usize, tab: usize, def: &hvm::Def, a: hvm::Port, b: &str) {\n  let a_node = compile_node(trg, code, book, neo, tab, def, a);\n  link_or_store(trg, code, book, neo, tab, def, &a_node, b);\n}\n\n// TODO: comment\npub fn link_or_store(trg: Target, code: &mut String, book: &hvm::Book, neo: &mut usize, tab: usize, def: &hvm::Def, a: &str, b: &str) {\n  code.push_str(&format!(\"{}if ({} != NONE) {{\\n\", indent(tab), b));\n  code.push_str(&format!(\"{}link(net, tm, {}, {});\\n\", indent(tab+1), a, b));\n  code.push_str(&format!(\"{}}} else {{\\n\", indent(tab)));\n  code.push_str(&format!(\"{}{} = {};\\n\", indent(tab+1), b, a));\n  code.push_str(&format!(\"{}}}\\n\", indent(tab)));\n}\n\n// Compiles just a node.\npub fn compile_node(trg: Target, code: &mut String, book: &hvm::Book, neo: &mut usize, tab: usize, def: &hvm::Def, a: hvm::Port) -> String {\n  if a.is_nod() {\n    let nd = &def.node[a.get_val() as usize];\n    let p1 = compile_node(trg, code, book, neo, tab, def, nd.get_fst());\n    let p2 = compile_node(trg, code, book, neo, tab, def, nd.get_snd());\n    code.push_str(&format!(\"{}node_create(net, n{:x}, new_pair({},{}));\\n\", indent(tab), a.get_val(), p1, p2));\n    return format!(\"new_port({},n{:x})\", compile_tag(trg, a.get_tag()), a.get_val());\n  } else if a.is_var() {\n    return format!(\"new_port(VAR,v{:x})\", a.get_val());\n  } else {\n    return format!(\"new_port({},0x{:08x})\", compile_tag(trg, a.get_tag()), a.get_val());\n  }\n}\n\n// Compiles an atomic port.\n//fn compile_atom(trg: Target, port: hvm::Port) -> String {\n  //return format!(\"new_port({},0x{:08x})/*atom*/\", compile_tag(trg, port.get_tag()), port.get_val());\n//}\n\n// Compiles a tag.\npub fn compile_tag(trg: Target, tag: hvm::Tag) -> &'static str {\n  match tag {\n    hvm::VAR => \"VAR\",\n    hvm::REF => \"REF\",\n    hvm::ERA => \"ERA\",\n    hvm::NUM => \"NUM\",\n    hvm::OPR => \"OPR\",\n    hvm::SWI => \"SWI\",\n    hvm::CON => \"CON\",\n    hvm::DUP => \"DUP\",\n    _ => unreachable!(),\n  }\n}\n\n// Creates indentation.\npub fn indent(tab: usize) -> String {\n  return \"  \".repeat(tab);\n}\n\n// Generates a fresh name.\nfn fresh(count: &mut usize) -> String {\n  *count += 1;\n  format!(\"k{}\", count)\n}\n"
  },
  {
    "path": "src/hvm.c",
    "content": "#include <inttypes.h>\n#include <math.h>\n#include <pthread.h>\n#include <stdatomic.h>\n#include <stdbool.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#ifdef DEBUG\n  #define debug(...) fprintf(stderr, __VA_ARGS__)\n#else\n  #define debug(...)\n#endif\n\n#define INTERPRETED\n#define WITHOUT_MAIN\n\n// Types\n// --------\n\ntypedef  uint8_t  u8;\ntypedef uint16_t u16;\ntypedef uint32_t u32;\ntypedef uint64_t u64;\ntypedef  int32_t i32;\ntypedef    float f32;\ntypedef   double f64;\n\ntypedef  _Atomic(u8)  a8;\ntypedef _Atomic(u16) a16;\ntypedef _Atomic(u32) a32;\ntypedef _Atomic(u64) a64;\n\n// Configuration\n// -------------\n\n// Threads per CPU\n#ifndef TPC_L2\n#define TPC_L2 0\n#endif\n#define TPC (1ul << TPC_L2)\n\n// Types\n// -----\n\n// Local Types\ntypedef u8  Tag;  // Tag  ::= 3-bit (rounded up to u8)\ntypedef u32 Val;  // Val  ::= 29-bit (rounded up to u32)\ntypedef u32 Port; // Port ::= Tag + Val (fits a u32)\ntypedef u64 Pair; // Pair ::= Port + Port (fits a u64)\n\ntypedef a32 APort; // atomic Port\ntypedef a64 APair; // atomic Pair\n\n// Rules\ntypedef u8 Rule; // Rule ::= 3-bit (rounded up to 8)\n\n// Numbs\ntypedef u32 Numb; // Numb ::= 29-bit (rounded up to u32)\n\n// Tags\n#define VAR 0x0 // variable\n#define REF 0x1 // reference\n#define ERA 0x2 // eraser\n#define NUM 0x3 // number\n#define CON 0x4 // constructor\n#define DUP 0x5 // duplicator\n#define OPR 0x6 // operator\n#define SWI 0x7 // switch\n\n// Interaction Rule Values\n#define LINK 0x0\n#define CALL 0x1\n#define VOID 0x2\n#define ERAS 0x3\n#define ANNI 0x4\n#define COMM 0x5\n#define OPER 0x6\n#define SWIT 0x7\n\n// Numbers\nstatic const f32 U24_MAX = (f32) (1 << 24) - 1;\nstatic const f32 U24_MIN = 0.0;\nstatic const f32 I24_MAX = (f32) (1 << 23) - 1;\nstatic const f32 I24_MIN = (f32) (i32) ((-1u) << 23);\n#define TY_SYM 0x00\n#define TY_U24 0x01\n#define TY_I24 0x02\n#define TY_F24 0x03\n#define OP_ADD 0x04\n#define OP_SUB 0x05\n#define FP_SUB 0x06\n#define OP_MUL 0x07\n#define OP_DIV 0x08\n#define FP_DIV 0x09\n#define OP_REM 0x0A\n#define FP_REM 0x0B\n#define OP_EQ  0x0C\n#define OP_NEQ 0x0D\n#define OP_LT  0x0E\n#define OP_GT  0x0F\n#define OP_AND 0x10\n#define OP_OR  0x11\n#define OP_XOR 0x12\n#define OP_SHL 0x13\n#define FP_SHL 0x14\n#define OP_SHR 0x15\n#define FP_SHR 0x16\n\n// Constants\n#define FREE 0x00000000\n#define ROOT 0xFFFFFFF8\n#define NONE 0xFFFFFFFF\n\n// Cache Padding\n#define CACHE_PAD 64\n\n// Global Net\n#define HLEN (1ul << 16) // max 16k high-priority redexes\n#define RLEN (1ul << 24) // max 16m low-priority redexes\n#define G_NODE_LEN (1ul << 29) // max 536m nodes\n#define G_VARS_LEN (1ul << 29) // max 536m vars\n#define G_RBAG_LEN (TPC * RLEN)\n\ntypedef struct Net {\n  APair node_buf[G_NODE_LEN]; // global node buffer\n  APort vars_buf[G_VARS_LEN]; // global vars buffer\n  APair rbag_buf[G_RBAG_LEN]; // global rbag buffer\n  a64 itrs; // interaction count\n  a32 idle; // idle thread counter\n} Net;\n\n#define DEF_RBAG_LEN 0xFFF\n#define DEF_NODE_LEN 0xFFF\n\n// Top-Level Definition\ntypedef struct Def {\n  char name[256];\n  bool safe;\n  u32  rbag_len;\n  u32  node_len;\n  u32  vars_len;\n  Port root;\n  Pair node_buf[DEF_NODE_LEN];\n  Pair rbag_buf[DEF_RBAG_LEN];\n} Def;\n\ntypedef struct Book Book;\n\n// A Foreign Function\ntypedef struct {\n  char name[256];\n  Port (*func)(Net*, Book*, Port);\n} FFn;\n\n// Book of Definitions\ntypedef struct Book {\n  u32 defs_len;\n  Def defs_buf[0x4000];\n  u32 ffns_len;\n  FFn ffns_buf[0x4000];\n} Book;\n\n// Local Thread Memory\ntypedef struct TM {\n  u32  tid; // thread id\n  u32  itrs; // interaction count\n  u32  nput; // next node allocation attempt index\n  u32  vput; // next vars allocation attempt index\n  u32  hput; // next hbag push index\n  u32  rput; // next rbag push index\n  u32  sidx; // steal index\n  u32  nloc[0xFFF]; // global node allocation indices\n  u32  vloc[0xFFF]; // global vars allocation indices\n  Pair hbag_buf[HLEN]; // high-priority redexes\n} TM;\n\n// Debugger\n// --------\n\ntypedef struct {\n  char x[13];\n} Show;\n\nvoid put_u16(char* B, u16 val);\nShow show_port(Port port);\nShow show_rule(Rule rule);\nvoid print_net(Net* net);\nvoid pretty_print_numb(Numb word);\nvoid pretty_print_port(Net* net, Book* book, Port port);\n\n// Port: Constructor and Getters\n// -----------------------------\n\nstatic inline Port new_port(Tag tag, Val val) {\n  return (val << 3) | tag;\n}\n\nstatic inline Tag get_tag(Port port) {\n  return port & 7;\n}\n\nstatic inline Val get_val(Port port) {\n  return port >> 3;\n}\n\n// Pair: Constructor and Getters\n// -----------------------------\n\nstatic inline const Pair new_pair(Port fst, Port snd) {\n  return ((u64)snd << 32) | fst;\n}\n\nstatic inline Port get_fst(Pair pair) {\n  return pair & 0xFFFFFFFF;\n}\n\nstatic inline Port get_snd(Pair pair) {\n  return pair >> 32;\n}\n\nPair set_par_flag(Pair pair) {\n  Port p1 = get_fst(pair);\n  Port p2 = get_snd(pair);\n  if (get_tag(p1) == REF) {\n    return new_pair(new_port(get_tag(p1), get_val(p1) | 0x10000000), p2);\n  } else {\n    return pair;\n  }\n}\n\nPair clr_par_flag(Pair pair) {\n  Port p1 = get_fst(pair);\n  Port p2 = get_snd(pair);\n  if (get_tag(p1) == REF) {\n    return new_pair(new_port(get_tag(p1), get_val(p1) & 0xFFFFFFF), p2);\n  } else {\n    return pair;\n  }\n}\n\nbool get_par_flag(Pair pair) {\n  Port p1 = get_fst(pair);\n  if (get_tag(p1) == REF) {\n    return (get_val(p1) >> 28) == 1;\n  } else {\n    return false;\n  }\n}\n\n// Utils\n// -----\n\n// Swaps two ports.\nstatic inline void swap(Port *a, Port *b) {\n  Port x = *a; *a = *b; *b = x;\n}\n\nstatic inline u32 min(u32 a, u32 b) {\n  return (a < b) ? a : b;\n}\n\nstatic inline f32 clamp(f32 x, f32 min, f32 max) {\n  const f32 t = x < min ? min : x;\n  return (t > max) ? max : t;\n}\n\n// A simple spin-wait barrier using atomic operations\na64 a_reached = 0; // number of threads that reached the current barrier\na64 a_barrier = 0; // number of barriers passed during this program\nvoid sync_threads() {\n  u64 barrier_old = atomic_load_explicit(&a_barrier, memory_order_relaxed);\n  if (atomic_fetch_add_explicit(&a_reached, 1, memory_order_relaxed) == (TPC - 1)) {\n    // Last thread to reach the barrier resets the counter and advances the barrier\n    atomic_store_explicit(&a_reached, 0, memory_order_relaxed);\n    atomic_store_explicit(&a_barrier, barrier_old + 1, memory_order_release);\n  } else {\n    u32 tries = 0;\n    while (atomic_load_explicit(&a_barrier, memory_order_acquire) == barrier_old) {\n      sched_yield();\n    }\n  }\n}\n\n// Global sum function\nstatic a32 GLOBAL_SUM = 0;\nu32 global_sum(u32 x) {\n  atomic_fetch_add_explicit(&GLOBAL_SUM, x, memory_order_relaxed);\n  sync_threads();\n  u32 sum = atomic_load_explicit(&GLOBAL_SUM, memory_order_relaxed);\n  sync_threads();\n  atomic_store_explicit(&GLOBAL_SUM, 0, memory_order_relaxed);\n  return sum;\n}\n\n// TODO: write a time64() function that returns the time as fast as possible as a u64\nstatic inline u64 time64() {\n  struct timespec ts;\n  clock_gettime(CLOCK_MONOTONIC, &ts);\n  return (u64)ts.tv_sec * 1000000000ULL + (u64)ts.tv_nsec;\n}\n\n// Ports / Pairs / Rules\n// ---------------------\n\n// True if this port has a pointer to a node.\nstatic inline bool is_nod(Port a) {\n  return get_tag(a) >= CON;\n}\n\n// True if this port is a variable.\nstatic inline bool is_var(Port a) {\n  return get_tag(a) == VAR;\n}\n\n// Given two tags, gets their interaction rule.\nstatic inline Rule get_rule(Port a, Port b) {\n  const u8 table[8][8] = {\n    //VAR  REF  ERA  NUM  CON  DUP  OPR  SWI\n    {LINK,LINK,LINK,LINK,LINK,LINK,LINK,LINK}, // VAR\n    {LINK,VOID,VOID,VOID,CALL,CALL,CALL,CALL}, // REF\n    {LINK,VOID,VOID,VOID,ERAS,ERAS,ERAS,ERAS}, // ERA\n    {LINK,VOID,VOID,VOID,ERAS,ERAS,OPER,SWIT}, // NUM\n    {LINK,CALL,ERAS,ERAS,ANNI,COMM,COMM,COMM}, // CON\n    {LINK,CALL,ERAS,ERAS,COMM,ANNI,COMM,COMM}, // DUP\n    {LINK,CALL,ERAS,OPER,COMM,COMM,ANNI,COMM}, // OPR\n    {LINK,CALL,ERAS,SWIT,COMM,COMM,COMM,ANNI}, // SWI\n  };\n  return table[get_tag(a)][get_tag(b)];\n}\n\n// Same as above, but receiving a pair.\nstatic inline Rule get_pair_rule(Pair AB) {\n  return get_rule(get_fst(AB), get_snd(AB));\n}\n\n// Should we swap ports A and B before reducing this rule?\nstatic inline bool should_swap(Port A, Port B) {\n  return get_tag(B) < get_tag(A);\n}\n\n// Gets a rule's priority\nstatic inline bool is_high_priority(Rule rule) {\n  // TODO: this needs to be more readable\n  return (bool)((0b00011101 >> rule) & 1);\n}\n\n// Adjusts a newly allocated port.\nstatic inline Port adjust_port(Net* net, TM* tm, Port port) {\n  Tag tag = get_tag(port);\n  Val val = get_val(port);\n  if (is_nod(port)) return new_port(tag, tm->nloc[val]);\n  if (is_var(port)) return new_port(tag, tm->vloc[val]);\n  return new_port(tag, val);\n}\n\n// Adjusts a newly allocated pair.\nstatic inline Pair adjust_pair(Net* net, TM* tm, Pair pair) {\n  Port p1 = adjust_port(net, tm, get_fst(pair));\n  Port p2 = adjust_port(net, tm, get_snd(pair));\n  return new_pair(p1, p2);\n}\n\n// Numbs\n// -----\n\n// Constructor and getters for SYM (operation selector)\nstatic inline Numb new_sym(u32 val) {\n  return (val << 5) | TY_SYM;\n}\n\nstatic inline u32 get_sym(Numb word) {\n  return (word >> 5);\n}\n\n// Constructor and getters for U24 (unsigned 24-bit integer)\nstatic inline Numb new_u24(u32 val) {\n  return (val << 5) | TY_U24;\n}\n\nstatic inline u32 get_u24(Numb word) {\n  return word >> 5;\n}\n\n// Constructor and getters for I24 (signed 24-bit integer)\nstatic inline Numb new_i24(i32 val) {\n  return ((u32)val << 5) | TY_I24;\n}\n\nstatic inline i32 get_i24(Numb word) {\n  return ((i32)word) << 3 >> 8;\n}\n\n// Constructor and getters for F24 (24-bit float)\nstatic inline Numb new_f24(float val) {\n  u32 bits = *(u32*)&val;\n  u32 shifted_bits = bits >> 8;\n  u32 lost_bits = bits & 0xFF;\n  // round ties to even\n  shifted_bits += (!isnan(val)) & ((lost_bits - ((lost_bits >> 7) & !shifted_bits)) >> 7);\n  // ensure NaNs don't become infinities\n  shifted_bits |= isnan(val);\n  return (shifted_bits << 5) | TY_F24;\n}\n\nstatic inline float get_f24(Numb word) {\n  u32 bits = (word << 3) & 0xFFFFFF00;\n  return *(float*)&bits;\n}\n\n// Flip flag\nstatic inline Tag get_typ(Numb word) {\n  return word & 0x1F;\n}\n\nstatic inline bool is_num(Numb word) {\n  return get_typ(word) >= TY_U24 && get_typ(word) <= TY_F24;\n}\n\nstatic inline bool is_cast(Numb word) {\n  return get_typ(word) == TY_SYM && get_sym(word) >= TY_U24 && get_sym(word) <= TY_F24;\n}\n\n// Partial application\nstatic inline Numb partial(Numb a, Numb b) {\n  return (b & ~0x1F) | get_sym(a);\n}\n\n// Cast a number to another type.\n// The semantics are meant to spiritually resemble rust's numeric casts:\n// - i24 <-> u24: is just reinterpretation of bits\n// - f24  -> i24,\n//   f24  -> u24: casts to the \"closest\" integer representing this float,\n//                saturating if out of range and 0 if NaN\n// - i24  -> f24,\n//   u24  -> f24: casts to the \"closest\" float representing this integer.\nstatic inline Numb cast(Numb a, Numb b) {\n  if (get_sym(a) == TY_U24 && get_typ(b) == TY_U24) return b;\n  if (get_sym(a) == TY_U24 && get_typ(b) == TY_I24) {\n    // reinterpret bits\n    i32 val = get_i24(b);\n    return new_u24(*(u32*) &val);\n  }\n  if (get_sym(a) == TY_U24 && get_typ(b) == TY_F24) {\n    f32 val = get_f24(b);\n    if (isnan(val)) {\n      return new_u24(0);\n    }\n    return new_u24((u32) clamp(val, U24_MIN, U24_MAX));\n  }\n\n  if (get_sym(a) == TY_I24 && get_typ(b) == TY_U24) {\n    // reinterpret bits\n    u32 val = get_u24(b);\n    return new_i24(*(i32*) &val);\n  }\n  if (get_sym(a) == TY_I24 && get_typ(b) == TY_I24) return b;\n  if (get_sym(a) == TY_I24 && get_typ(b) == TY_F24) {\n    f32 val = get_f24(b);\n    if (isnan(val)) {\n      return new_i24(0);\n    }\n    return new_i24((i32) clamp(val, I24_MIN, I24_MAX));\n  }\n\n  if (get_sym(a) == TY_F24 && get_typ(b) == TY_U24) return new_f24((f32) get_u24(b));\n  if (get_sym(a) == TY_F24 && get_typ(b) == TY_I24) return new_f24((f32) get_i24(b));\n  if (get_sym(a) == TY_F24 && get_typ(b) == TY_F24) return b;\n\n  return new_u24(0);\n}\n\n// Operate function\nstatic inline Numb operate(Numb a, Numb b) {\n  Tag at = get_typ(a);\n  Tag bt = get_typ(b);\n  if (at == TY_SYM && bt == TY_SYM) {\n    return new_u24(0);\n  }\n  if (is_cast(a) && is_num(b)) {\n    return cast(a, b);\n  }\n  if (is_cast(b) && is_num(a)) {\n    return cast(b, a);\n  }\n  if (at == TY_SYM && bt != TY_SYM) {\n    return partial(a, b);\n  }\n  if (at != TY_SYM && bt == TY_SYM) {\n    return partial(b, a);\n  }\n  if (at >= OP_ADD && bt >= OP_ADD) {\n    return new_u24(0);\n  }\n  if (at < OP_ADD && bt < OP_ADD) {\n    return new_u24(0);\n  }\n  Tag op, ty;\n  Numb swp;\n  if (at >= OP_ADD) {\n    op = at; ty = bt;\n  } else {\n    op = bt; ty = at; swp = a; a = b; b = swp;\n  }\n  switch (ty) {\n    case TY_U24: {\n      u32 av = get_u24(a);\n      u32 bv = get_u24(b);\n      switch (op) {\n        case OP_ADD: return new_u24(av + bv);\n        case OP_SUB: return new_u24(av - bv);\n        case FP_SUB: return new_u24(bv - av);\n        case OP_MUL: return new_u24(av * bv);\n        case OP_DIV: return new_u24(av / bv);\n        case FP_DIV: return new_u24(bv / av);\n        case OP_REM: return new_u24(av % bv);\n        case FP_REM: return new_u24(bv % av);\n        case OP_EQ:  return new_u24(av == bv);\n        case OP_NEQ: return new_u24(av != bv);\n        case OP_LT:  return new_u24(av < bv);\n        case OP_GT:  return new_u24(av > bv);\n        case OP_AND: return new_u24(av & bv);\n        case OP_OR:  return new_u24(av | bv);\n        case OP_XOR: return new_u24(av ^ bv);\n        case OP_SHL: return new_u24(av << (bv & 31));\n        case FP_SHL: return new_u24(bv << (av & 31));\n        case OP_SHR: return new_u24(av >> (bv & 31));\n        case FP_SHR: return new_u24(bv >> (av & 31));\n        default:     return new_u24(0);\n      }\n    }\n    case TY_I24: {\n      i32 av = get_i24(a);\n      i32 bv = get_i24(b);\n      switch (op) {\n        case OP_ADD: return new_i24(av + bv);\n        case OP_SUB: return new_i24(av - bv);\n        case FP_SUB: return new_i24(bv - av);\n        case OP_MUL: return new_i24(av * bv);\n        case OP_DIV: return new_i24(av / bv);\n        case FP_DIV: return new_i24(bv / av);\n        case OP_REM: return new_i24(av % bv);\n        case FP_REM: return new_i24(bv % av);\n        case OP_EQ:  return new_u24(av == bv);\n        case OP_NEQ: return new_u24(av != bv);\n        case OP_LT:  return new_u24(av < bv);\n        case OP_GT:  return new_u24(av > bv);\n        case OP_AND: return new_i24(av & bv);\n        case OP_OR:  return new_i24(av | bv);\n        case OP_XOR: return new_i24(av ^ bv);\n        default:     return new_i24(0);\n      }\n    }\n    case TY_F24: {\n      float av = get_f24(a);\n      float bv = get_f24(b);\n      switch (op) {\n        case OP_ADD: return new_f24(av + bv);\n        case OP_SUB: return new_f24(av - bv);\n        case FP_SUB: return new_f24(bv - av);\n        case OP_MUL: return new_f24(av * bv);\n        case OP_DIV: return new_f24(av / bv);\n        case FP_DIV: return new_f24(bv / av);\n        case OP_REM: return new_f24(fmodf(av, bv));\n        case FP_REM: return new_f24(fmodf(bv, av));\n        case OP_EQ:  return new_u24(av == bv);\n        case OP_NEQ: return new_u24(av != bv);\n        case OP_LT:  return new_u24(av < bv);\n        case OP_GT:  return new_u24(av > bv);\n        case OP_AND: return new_f24(atan2f(av, bv));\n        case OP_OR:  return new_f24(logf(bv) / logf(av));\n        case OP_XOR: return new_f24(powf(av, bv));\n        case OP_SHL: return new_f24(sin(av + bv));\n        case OP_SHR: return new_f24(tan(av + bv));\n        default:     return new_f24(0);\n      }\n    }\n    default: return new_u24(0);\n  }\n}\n\n// RBag\n// ----\n\n// FIXME: what about some bound checks?\n\nstatic inline void push_redex(Net* net, TM* tm, Pair redex) {\n  #ifdef DEBUG\n  bool free_local = tm->hput < HLEN;\n  bool free_global = tm->rput < RLEN;\n  if (!free_global || !free_local) {\n    debug(\"push_redex: limited resources, maybe corrupting memory\\n\");\n  }\n  #endif\n\n  if (is_high_priority(get_pair_rule(redex))) {\n    tm->hbag_buf[tm->hput++] = redex;\n  } else {\n    atomic_store_explicit(&net->rbag_buf[tm->tid*(G_RBAG_LEN/TPC) + (tm->rput++)], redex, memory_order_relaxed);\n  }\n}\n\nstatic inline Pair pop_redex(Net* net, TM* tm) {\n  if (tm->hput > 0) {\n    return tm->hbag_buf[--tm->hput];\n  } else if (tm->rput > 0) {\n    return atomic_exchange_explicit(&net->rbag_buf[tm->tid*(G_RBAG_LEN/TPC) + (--tm->rput)], 0, memory_order_relaxed);\n  } else {\n    return 0;\n  }\n}\n\nstatic inline u32 rbag_len(Net* net, TM* tm) {\n  return tm->rput + tm->hput;\n}\n\n// TM\n// --\n\nstatic TM* tm[TPC];\n\nTM* tm_new(u32 tid) {\n  TM* tm   = malloc(sizeof(TM));\n  tm->tid  = tid;\n  tm->itrs = 0;\n  tm->nput = 1;\n  tm->vput = 1;\n  tm->rput = 0;\n  tm->hput = 0;\n  tm->sidx = 0;\n  return tm;\n}\n\nvoid alloc_static_tms() {\n  for (u32 t = 0; t < TPC; ++t) {\n    tm[t] = tm_new(t);\n  }\n}\n\nvoid free_static_tms() {\n  for (u32 t = 0; t < TPC; ++t) {\n    free(tm[t]);\n  }\n}\n\n// Net\n// ----\n\n// Stores a new node on global.\nstatic inline void node_create(Net* net, u32 loc, Pair val) {\n  atomic_store_explicit(&net->node_buf[loc], val, memory_order_relaxed);\n}\n\n// Stores a var on global.\nstatic inline void vars_create(Net* net, u32 var, Port val) {\n  atomic_store_explicit(&net->vars_buf[var], val, memory_order_relaxed);\n}\n\n// Reads a node from global.\nstatic inline Pair node_load(Net* net, u32 loc) {\n  return atomic_load_explicit(&net->node_buf[loc], memory_order_relaxed);\n}\n\n// Reads a var from global.\nstatic inline Port vars_load(Net* net, u32 var) {\n  return atomic_load_explicit(&net->vars_buf[var], memory_order_relaxed);\n}\n\n// Stores a node on global.\nstatic inline void node_store(Net* net, u32 loc, Pair val) {\n  atomic_store_explicit(&net->node_buf[loc], val, memory_order_relaxed);\n}\n\n// Exchanges a node on global by a value. Returns old.\nstatic inline Pair node_exchange(Net* net, u32 loc, Pair val) {\n  return atomic_exchange_explicit(&net->node_buf[loc], val, memory_order_relaxed);\n}\n\n// Exchanges a var on global by a value. Returns old.\nstatic inline Port vars_exchange(Net* net, u32 var, Port val) {\n  return atomic_exchange_explicit(&net->vars_buf[var], val, memory_order_relaxed);\n}\n\n// Takes a node.\nstatic inline Pair node_take(Net* net, u32 loc) {\n  return node_exchange(net, loc, 0);\n}\n\n// Takes a var.\nstatic inline Port vars_take(Net* net, u32 var) {\n  return vars_exchange(net, var, 0);\n}\n\n\n// Net\n// ---\n\n// Initializes a net.\nstatic inline Net* net_new() {\n  Net* net = calloc(1, sizeof(Net));\n\n  atomic_store(&net->itrs, 0);\n  atomic_store(&net->idle, 0);\n\n  return net;\n}\n\n// Allocator\n// ---------\n\nu32 node_alloc_1(Net* net, TM* tm, u32* lps) {\n  while (true) {\n    u32 lc = tm->tid*(G_NODE_LEN/TPC) + (tm->nput%(G_NODE_LEN/TPC));\n    Pair elem = net->node_buf[lc];\n    tm->nput += 1;\n    if (lc > 0 && elem == 0) {\n      return lc;\n    }\n    // FIXME: check this decently\n    if (++(*lps) >= G_NODE_LEN/TPC) printf(\"OOM\\n\");\n  }\n}\n\nu32 vars_alloc_1(Net* net, TM* tm, u32* lps) {\n  while (true) {\n    u32 lc = tm->tid*(G_NODE_LEN/TPC) + (tm->vput%(G_NODE_LEN/TPC));\n    Port elem = net->vars_buf[lc];\n    tm->vput += 1;\n    if (lc > 0 && elem == 0) {\n      return lc;\n    }\n    // FIXME: check this decently\n    if (++(*lps) >= G_NODE_LEN/TPC) printf(\"OOM\\n\");\n  }\n}\n\nu32 node_alloc(Net* net, TM* tm, u32 num) {\n  u32 got = 0;\n  u32 lps = 0;\n  while (got < num) {\n    u32 lc = tm->tid*(G_NODE_LEN/TPC) + (tm->nput%(G_NODE_LEN/TPC));\n    Pair elem = net->node_buf[lc];\n    tm->nput += 1;\n    if (lc > 0 && elem == 0) {\n      tm->nloc[got++] = lc;\n    }\n    // FIXME: check this decently\n    if (++lps >= G_NODE_LEN/TPC) printf(\"OOM\\n\");\n  }\n  return got;\n}\n\nu32 vars_alloc(Net* net, TM* tm, u32 num) {\n  u32 got = 0;\n  u32 lps = 0;\n  while (got < num) {\n    u32 lc = tm->tid*(G_NODE_LEN/TPC) + (tm->vput%(G_NODE_LEN/TPC));\n    Port elem = net->vars_buf[lc];\n    tm->vput += 1;\n    if (lc > 0 && elem == 0) {\n      tm->vloc[got++] = lc;\n    }\n    // FIXME: check this decently\n    if (++lps >= G_NODE_LEN/TPC) printf(\"OOM\\n\");\n  }\n  return got;\n}\n\n// Gets the necessary resources for an interaction. Returns success.\nstatic inline bool get_resources(Net* net, TM* tm, u32 need_rbag, u32 need_node, u32 need_vars) {\n  u32 got_rbag = min(RLEN - tm->rput, HLEN - tm->hput);\n  u32 got_node = node_alloc(net, tm, need_node);\n  u32 got_vars = vars_alloc(net, tm, need_vars);\n\n  return got_rbag >= need_rbag && got_node >= need_node && got_vars >= need_vars;\n}\n\n// Linking\n// -------\n\n// Peeks a variable's final target without modifying it.\nstatic inline Port peek(Net* net, Port var) {\n  while (get_tag(var) == VAR) {\n    Port val = vars_load(net, get_val(var));\n    if (val == NONE) break;\n    if (val == 0) break;\n    var = val;\n  }\n  return var;\n}\n\n// Finds a variable's value.\nstatic inline Port enter(Net* net, Port var) {\n  // While `B` is VAR: extend it (as an optimization)\n  while (get_tag(var) == VAR) {\n    // Takes the current `var` substitution as `val`\n    Port val = vars_exchange(net, get_val(var), NONE);\n    // If there was no `val`, stop, as there is no extension\n    if (val == NONE || val == 0) {\n      break;\n    }\n    // Otherwise, delete `B` (we own both) and continue\n    vars_take(net, get_val(var));\n    var = val;\n  }\n  return var;\n}\n\n// Atomically Links `A ~ B`.\nstatic inline void link(Net* net, TM* tm, Port A, Port B) {\n  // Attempts to directionally point `A ~> B`\n  while (true) {\n    // If `A` is NODE: swap `A` and `B`, and continue\n    if (get_tag(A) != VAR && get_tag(B) == VAR) {\n      Port X = A; A = B; B = X;\n    }\n\n    // If `A` is NODE: create the `A ~ B` redex\n    if (get_tag(A) != VAR) {\n      push_redex(net, tm, new_pair(A, B)); // TODO: move global ports to local\n      break;\n    }\n\n    // Extends B (as an optimization)\n    B = enter(net, B);\n\n    // Since `A` is VAR: point `A ~> B`.\n    // Stores `A -> B`, taking the current `A` subst as `A'`\n    Port A_ = vars_exchange(net, get_val(A), B);\n    // If there was no `A'`, stop, as we lost B's ownership\n    if (A_ == NONE) {\n      break;\n    }\n    //if (A_ == 0) { ? } // FIXME: must handle on the move-to-global algo\n    // Otherwise, delete `A` (we own both) and link `A' ~ B`\n    vars_take(net, get_val(A));\n    A = A_;\n  }\n}\n\n// Links `A ~ B` (as a pair).\nstatic inline void link_pair(Net* net, TM* tm, Pair AB) {\n  link(net, tm, get_fst(AB), get_snd(AB));\n}\n\n// Interactions\n// ------------\n\n// The Link Interaction.\nstatic inline bool interact_link(Net* net, TM* tm, Port a, Port b) {\n  // Allocates needed nodes and vars.\n  if (!get_resources(net, tm, 1, 0, 0)) {\n    debug(\"interact_link: get_resources failed\\n\");\n    return false;\n  }\n\n  // Links.\n  link_pair(net, tm, new_pair(a, b));\n\n  return true;\n}\n\n// Declared here for use in call interactions.\nstatic inline bool interact_eras(Net* net, TM* tm, Port a, Port b);\n\n// The Call Interaction.\n#ifdef COMPILED\n///COMPILED_INTERACT_CALL///\n#else\nstatic inline bool interact_call(Net* net, TM* tm, Port a, Port b, Book* book) {\n  // Loads Definition.\n  u32  fid = get_val(a) & 0xFFFFFFF;\n  Def* def = &book->defs_buf[fid];\n\n  // Copy Optimization.\n  if (def->safe && get_tag(b) == DUP) {\n    return interact_eras(net, tm, a, b);\n  }\n\n  // Allocates needed nodes and vars.\n  if (!get_resources(net, tm, def->rbag_len + 1, def->node_len, def->vars_len)) {\n    debug(\"interact_call: get_resources failed\\n\");\n    return false;\n  }\n\n  // Stores new vars.\n  for (u32 i = 0; i < def->vars_len; ++i) {\n    vars_create(net, tm->vloc[i], NONE);\n  }\n\n  // Stores new nodes.\n  for (u32 i = 0; i < def->node_len; ++i) {\n    node_create(net, tm->nloc[i], adjust_pair(net, tm, def->node_buf[i]));\n  }\n\n  // Links.\n  for (u32 i = 0; i < def->rbag_len; ++i) {\n    link_pair(net, tm, adjust_pair(net, tm, def->rbag_buf[i]));\n  }\n  link_pair(net, tm, new_pair(adjust_port(net, tm, def->root), b));\n\n  return true;\n}\n#endif\n\n// The Void Interaction.\nstatic inline bool interact_void(Net* net, TM* tm, Port a, Port b) {\n  return true;\n}\n\n// The Eras Interaction.\nstatic inline bool interact_eras(Net* net, TM* tm, Port a, Port b) {\n  // Allocates needed nodes and vars.\n  if (!get_resources(net, tm, 2, 0, 0)) {\n    debug(\"interact_eras: get_resources failed\\n\");\n    return false;\n  }\n\n  // Checks availability\n  if (node_load(net, get_val(b)) == 0) {\n    return false;\n  }\n\n  // Loads ports.\n  Pair B  = node_exchange(net, get_val(b), 0);\n  Port B1 = get_fst(B);\n  Port B2 = get_snd(B);\n\n  // Links.\n  link_pair(net, tm, new_pair(a, B1));\n  link_pair(net, tm, new_pair(a, B2));\n\n  return true;\n}\n\n// The Anni Interaction.\nstatic inline bool interact_anni(Net* net, TM* tm, Port a, Port b) {\n  // Allocates needed nodes and vars.\n  if (!get_resources(net, tm, 2, 0, 0)) {\n    debug(\"interact_anni: get_resources failed\\n\");\n    return false;\n  }\n\n  // Checks availability\n  if (node_load(net, get_val(a)) == 0 || node_load(net, get_val(b)) == 0) {\n    return false;\n  }\n\n  // Loads ports.\n  Pair A  = node_take(net, get_val(a));\n  Port A1 = get_fst(A);\n  Port A2 = get_snd(A);\n  Pair B  = node_take(net, get_val(b));\n  Port B1 = get_fst(B);\n  Port B2 = get_snd(B);\n\n  // Links.\n  link_pair(net, tm, new_pair(A1, B1));\n  link_pair(net, tm, new_pair(A2, B2));\n\n  return true;\n}\n\n// The Comm Interaction.\nstatic inline bool interact_comm(Net* net, TM* tm, Port a, Port b) {\n  // Allocates needed nodes and vars.\n  if (!get_resources(net, tm, 4, 4, 4)) {\n    debug(\"interact_comm: get_resources failed\\n\");\n    return false;\n  }\n\n  // Checks availability\n  if (node_load(net, get_val(a)) == 0 || node_load(net, get_val(b)) == 0) {\n    return false;\n  }\n\n  // Loads ports.\n  Pair A  = node_take(net, get_val(a));\n  Port A1 = get_fst(A);\n  Port A2 = get_snd(A);\n  Pair B  = node_take(net, get_val(b));\n  Port B1 = get_fst(B);\n  Port B2 = get_snd(B);\n\n  // Stores new vars.\n  vars_create(net, tm->vloc[0], NONE);\n  vars_create(net, tm->vloc[1], NONE);\n  vars_create(net, tm->vloc[2], NONE);\n  vars_create(net, tm->vloc[3], NONE);\n\n  // Stores new nodes.\n  node_create(net, tm->nloc[0], new_pair(new_port(VAR, tm->vloc[0]), new_port(VAR, tm->vloc[1])));\n  node_create(net, tm->nloc[1], new_pair(new_port(VAR, tm->vloc[2]), new_port(VAR, tm->vloc[3])));\n  node_create(net, tm->nloc[2], new_pair(new_port(VAR, tm->vloc[0]), new_port(VAR, tm->vloc[2])));\n  node_create(net, tm->nloc[3], new_pair(new_port(VAR, tm->vloc[1]), new_port(VAR, tm->vloc[3])));\n\n  // Links.\n  link_pair(net, tm, new_pair(new_port(get_tag(b), tm->nloc[0]), A1));\n  link_pair(net, tm, new_pair(new_port(get_tag(b), tm->nloc[1]), A2));\n  link_pair(net, tm, new_pair(new_port(get_tag(a), tm->nloc[2]), B1));\n  link_pair(net, tm, new_pair(new_port(get_tag(a), tm->nloc[3]), B2));\n\n  return true;\n}\n\n// The Oper Interaction.\nstatic inline bool interact_oper(Net* net, TM* tm, Port a, Port b) {\n  // Allocates needed nodes and vars.\n  if (!get_resources(net, tm, 1, 1, 0)) {\n    debug(\"interact_oper: get_resources failed\\n\");\n    return false;\n  }\n\n  // Checks availability\n  if (node_load(net, get_val(b)) == 0) {\n    return false;\n  }\n\n  // Loads ports.\n  Val  av = get_val(a);\n  Pair B  = node_take(net, get_val(b));\n  Port B1 = get_fst(B);\n  Port B2 = enter(net, get_snd(B));\n\n  // Performs operation.\n  if (get_tag(B1) == NUM) {\n    Val  bv = get_val(B1);\n    Numb cv = operate(av, bv);\n    link_pair(net, tm, new_pair(new_port(NUM, cv), B2));\n  } else {\n    node_create(net, tm->nloc[0], new_pair(a, B2));\n    link_pair(net, tm, new_pair(B1, new_port(OPR, tm->nloc[0])));\n  }\n\n  return true;\n}\n\n// The Swit Interaction.\nstatic inline bool interact_swit(Net* net, TM* tm, Port a, Port b) {\n  // Allocates needed nodes and vars.\n  if (!get_resources(net, tm, 1, 2, 0)) {\n    debug(\"interact_swit: get_resources failed\\n\");\n    return false;\n  }\n\n  // Checks availability\n  if (node_load(net, get_val(b)) == 0) {\n    return false;\n  }\n\n  // Loads ports.\n  u32  av = get_u24(get_val(a));\n  Pair B  = node_take(net, get_val(b));\n  Port B1 = get_fst(B);\n  Port B2 = get_snd(B);\n\n  // Stores new nodes.\n  if (av == 0) {\n    node_create(net, tm->nloc[0], new_pair(B2, new_port(ERA,0)));\n    link_pair(net, tm, new_pair(new_port(CON, tm->nloc[0]), B1));\n  } else {\n    node_create(net, tm->nloc[0], new_pair(new_port(ERA,0), new_port(CON, tm->nloc[1])));\n    node_create(net, tm->nloc[1], new_pair(new_port(NUM, new_u24(av-1)), B2));\n    link_pair(net, tm, new_pair(new_port(CON, tm->nloc[0]), B1));\n  }\n\n  return true;\n}\n\n// Pops a local redex and performs a single interaction.\nstatic inline bool interact(Net* net, TM* tm, Book* book) {\n  // Pops a redex.\n  Pair redex = pop_redex(net, tm);\n\n  // If there is no redex, stop.\n  if (redex != 0) {\n    // Gets redex ports A and B.\n    Port a = get_fst(redex);\n    Port b = get_snd(redex);\n\n    // Gets the rule type.\n    Rule rule = get_rule(a, b);\n\n    // Used for root redex.\n    if (get_tag(a) == REF && b == ROOT) {\n      rule = CALL;\n    // Swaps ports if necessary.\n    } else if (should_swap(a,b)) {\n      swap(&a, &b);\n    }\n\n    // Dispatches interaction rule.\n    bool success;\n    switch (rule) {\n      case LINK: success = interact_link(net, tm, a, b); break;\n      #ifdef COMPILED\n      case CALL: success = interact_call(net, tm, a, b); break;\n      #else\n      case CALL: success = interact_call(net, tm, a, b, book); break;\n      #endif\n      case VOID: success = interact_void(net, tm, a, b); break;\n      case ERAS: success = interact_eras(net, tm, a, b); break;\n      case ANNI: success = interact_anni(net, tm, a, b); break;\n      case COMM: success = interact_comm(net, tm, a, b); break;\n      case OPER: success = interact_oper(net, tm, a, b); break;\n      case SWIT: success = interact_swit(net, tm, a, b); break;\n    }\n\n    // If error, pushes redex back.\n    if (!success) {\n      push_redex(net, tm, redex);\n      return false;\n    // Else, increments the interaction count.\n    } else if (rule != LINK) {\n      tm->itrs += 1;\n    }\n  }\n\n  return true;\n}\n\n// Evaluator\n// ---------\n\nvoid evaluator(Net* net, TM* tm, Book* book) {\n  // Initializes the global idle counter\n  atomic_store_explicit(&net->idle, TPC - 1, memory_order_relaxed);\n  sync_threads();\n\n  // Performs some interactions\n  u32  tick = 0;\n  bool busy = tm->tid == 0;\n  while (true) {\n    tick += 1;\n\n    // If we have redexes...\n    if (rbag_len(net, tm) > 0) {\n      // Update global idle counter\n      if (!busy) atomic_fetch_sub_explicit(&net->idle, 1, memory_order_relaxed);\n      busy = true;\n\n      // Perform an interaction\n      #ifdef DEBUG\n      if (!interact(net, tm, book)) debug(\"interaction failed\\n\");\n      #else\n      interact(net, tm, book);\n      #endif\n    // If we have no redexes...\n    } else {\n      // Update global idle counter\n      if (busy) atomic_fetch_add_explicit(&net->idle, 1, memory_order_relaxed);\n      busy = false;\n\n      //// Peeks a redex from target\n      u32 sid = (tm->tid - 1) % TPC;\n      u32 idx = sid*(G_RBAG_LEN/TPC) + (tm->sidx++);\n\n      // Stealing Everything: this will steal all redexes\n\n      Pair got = atomic_exchange_explicit(&net->rbag_buf[idx], 0, memory_order_relaxed);\n      if (got != 0) {\n        push_redex(net, tm, got);\n        continue;\n      } else {\n        tm->sidx = 0;\n      }\n\n      // Chill...\n      sched_yield();\n      // Halt if all threads are idle\n      if (tick % 256 == 0) {\n        if (atomic_load_explicit(&net->idle, memory_order_relaxed) == TPC) {\n          break;\n        }\n      }\n    }\n  }\n\n  sync_threads();\n\n  atomic_fetch_add(&net->itrs, tm->itrs);\n  tm->itrs = 0;\n}\n\n// Normalizer\n// ----------\n\n// Thread data\ntypedef struct {\n  Net*  net;\n  TM*   tm;\n  Book* book;\n} ThreadArg;\n\nvoid* thread_func(void* arg) {\n  ThreadArg* data = (ThreadArg*)arg;\n  evaluator(data->net, data->tm, data->book);\n  return NULL;\n}\n\n// Sets the initial redex.\nvoid boot_redex(Net* net, Pair redex) {\n  net->vars_buf[get_val(ROOT)] = NONE;\n  net->rbag_buf[0] = redex;\n}\n\n// Evaluates all redexes.\n// TODO: cache threads to avoid spawning overhead\nvoid normalize(Net* net, Book* book) {\n  // Inits thread_arg objects\n  ThreadArg thread_arg[TPC];\n  for (u32 t = 0; t < TPC; ++t) {\n    thread_arg[t].net  = net;\n    thread_arg[t].tm   = tm[t];\n    thread_arg[t].book = book;\n  }\n\n  // Spawns the evaluation threads\n  pthread_t threads[TPC];\n  for (u32 t = 0; t < TPC; ++t) {\n    pthread_create(&threads[t], NULL, thread_func, &thread_arg[t]);\n  }\n\n  // Wait for the threads to finish\n  for (u32 t = 0; t < TPC; ++t) {\n    pthread_join(threads[t], NULL);\n  }\n}\n\n// Util: expands a REF Port.\nPort expand(Net* net, Book* book, Port port) {\n  Port old = vars_load(net, get_val(ROOT));\n  Port got = peek(net, port);\n  while (get_tag(got) == REF) {\n    boot_redex(net, new_pair(got, ROOT));\n    normalize(net, book);\n    got = peek(net, vars_load(net, get_val(ROOT)));\n  }\n  vars_create(net, get_val(ROOT), old);\n  return got;\n}\n\n// Reads back an image.\n// Encoding: (<tree>,<tree>) | #RRGGBB\nvoid read_img(Net* net, Port port, u32 width, u32 height, u32* buffer) {\n  //pretty_print_port(net, port);\n  //printf(\"\\n\");\n  typedef struct {\n    Port port; u32 lv;\n    u32 x0; u32 x1;\n    u32 y0; u32 y1;\n  } Rect;\n  Rect stk[24];\n  u32 pos = 0;\n  stk[pos++] = (Rect){port, 0, 0, width, 0, height};\n  while (pos > 0) {\n    Rect rect = stk[--pos];\n    Port port = enter(net, rect.port);\n    u32  lv   = rect.lv;\n    u32  x0   = rect.x0;\n    u32  x1   = rect.x1;\n    u32  y0   = rect.y0;\n    u32  y1   = rect.y1;\n    if (get_tag(port) == CON) {\n      Pair nd = node_load(net, get_val(port));\n      Port p1 = get_fst(nd);\n      Port p2 = get_snd(nd);\n      u32  xm = (x0 + x1) / 2;\n      u32  ym = (y0 + y1) / 2;\n      if (lv % 2 == 0) {\n        stk[pos++] = (Rect){p2, lv+1, xm, x1, y0, y1};\n        stk[pos++] = (Rect){p1, lv+1, x0, xm, y0, y1};\n      } else {\n        stk[pos++] = (Rect){p2, lv+1, x0, x1, ym, y1};\n        stk[pos++] = (Rect){p1, lv+1, x0, x1, y0, ym};\n      }\n      continue;\n    }\n    if (get_tag(port) == NUM) {\n      u32 color = get_u24(get_val(port));\n      printf(\"COL=%08x x0=%04u x1=%04u y0=%04u y1=%04u | %s\\n\", color, x0, x1, y0, y1, show_port(port).x);\n      for (u32 y = y0; y < y1; y++) {\n        for (u32 x = x0; x < x1; x++) {\n          buffer[y*width + x] = 0xFF000000 | color;\n        }\n      }\n      continue;\n    }\n    break;\n  }\n}\n\n\n//#ifdef IO_DRAWIMAGE\n//// Global variables for the window and renderer\n//static SDL_Window *window = NULL;\n//static SDL_Renderer *renderer = NULL;\n//static SDL_Texture *texture = NULL;\n//// Function to close the SDL window and clean up resources\n//void close_sdl(void) {\n  //if (texture != NULL) {\n    //SDL_DestroyTexture(texture);\n    //texture = NULL;\n  //}\n  //if (renderer != NULL) {\n    //SDL_DestroyRenderer(renderer);\n    //renderer = NULL;\n  //}\n  //if (window != NULL) {\n    //SDL_DestroyWindow(window);\n    //window = NULL;\n  //}\n  //SDL_Quit();\n//}\n//// Function to render an image to the SDL window\n//void render(uint32_t width, uint32_t height, uint32_t *buffer) {\n  //// Initialize SDL if it hasn't been initialized\n  //if (SDL_WasInit(SDL_INIT_VIDEO) == 0) {\n    //if (SDL_Init(SDL_INIT_VIDEO) < 0) {\n      //fprintf(stderr, \"SDL could not initialize! SDL Error: %s\\n\", SDL_GetError());\n      //return;\n    //}\n  //}\n  //// Create window and renderer if they don't exist\n  //if (window == NULL) {\n    //window = SDL_CreateWindow(\"SDL Window\", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, SDL_WINDOW_SHOWN);\n    //if (window == NULL) {\n      //fprintf(stderr, \"Window could not be created! SDL Error: %s\\n\", SDL_GetError());\n      //return;\n    //}\n    //renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);\n    //if (renderer == NULL) {\n      //SDL_DestroyWindow(window);\n      //window = NULL;\n      //fprintf(stderr, \"Renderer could not be created! SDL Error: %s\\n\", SDL_GetError());\n      //return;\n    //}\n  //}\n  //// Create or recreate the texture if necessary\n  //if (texture == NULL) {\n    //texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height);\n    //if (texture == NULL) {\n      //fprintf(stderr, \"Texture could not be created! SDL Error: %s\\n\", SDL_GetError());\n      //return;\n    //}\n  //}\n  //// Update the texture with the new buffer\n  //if (SDL_UpdateTexture(texture, NULL, buffer, width * sizeof(uint32_t)) < 0) {\n    //fprintf(stderr, \"Texture could not be updated! SDL Error: %s\\n\", SDL_GetError());\n    //return;\n  //}\n  //// Clear the renderer\n  //SDL_RenderClear(renderer);\n  //// Copy the texture to the renderer\n  //SDL_RenderCopy(renderer, texture, NULL, NULL);\n  //// Update the screen\n  //SDL_RenderPresent(renderer);\n  //// Process events to prevent the OS from thinking the application is unresponsive\n  //SDL_Event e;\n  //while (SDL_PollEvent(&e)) {\n    //if (e.type == SDL_QUIT) {\n      //close_sdl();\n      //exit(0);\n    //}\n  //}\n//}\n//// IO: DrawImage\n//Port io_put_image(Net* net, Book* book, u32 argc, Port* argv) {\n  //u32 width = 256;\n  //u32 height = 256;\n  //// Create a buffer\n  //uint32_t *buffer = (uint32_t *)malloc(width * height * sizeof(uint32_t));\n  //if (buffer == NULL) {\n    //fprintf(stderr, \"Failed to allocate memory for buffer\\n\");\n    //return 1;\n  //}\n  //// Initialize buffer to a dark blue background\n  //for (int i = 0; i < width * height; ++i) {\n    //buffer[i] = 0xFF000030; // Dark blue background\n  //}\n  //// Converts a HVM2 tuple-encoded quadtree to a color buffer\n  //read_img(net, argv[0], width, height, buffer);\n  //// Render the buffer to the screen\n  //render(width, height, buffer);\n  //// Wait some time\n  //SDL_Delay(2000);\n  //// Free the buffer\n  //free(buffer);\n  //return new_port(ERA, 0);\n//}\n//#else\n//// IO: DrawImage\n//Port io_put_image(Net* net, Book* book, u32 argc, Port* argv) {\n  //printf(\"DRAWIMAGE: disabled.\\n\");\n  //printf(\"Image rendering is a WIP. For now, to enable it, you must:\\n\");\n  //printf(\"1. Generate a C file, with `hvm gen-c your_file.hvm`.\\n\");\n  //printf(\"2. Manually un-comment the '#define IO_DRAWIMAGE' line on it.\\n\");\n  //printf(\"3. Have SDL installed and compile it with '-lSDL2'.\\n\");\n  //return new_port(ERA, 0);\n//}\n//#endif\n\n// Book Loader\n// -----------\n\nbool book_load(Book* book, u32* buf) {\n  // Reads defs_len\n  book->defs_len = *buf++;\n\n  // Parses each def\n  for (u32 i = 0; i < book->defs_len; ++i) {\n    // Reads fid\n    u32 fid = *buf++;\n\n    // Gets def\n    Def* def = &book->defs_buf[fid];\n\n    // Reads name\n    memcpy(def->name, buf, 256);\n    buf += 64;\n\n    // Reads safe flag\n    def->safe = *buf++;\n\n    // Reads lengths\n    def->rbag_len = *buf++;\n    def->node_len = *buf++;\n    def->vars_len = *buf++;\n\n    if (def->rbag_len > DEF_RBAG_LEN) {\n      fprintf(stderr, \"def '%s' has too many redexes: %u\\n\", def->name, def->rbag_len);\n      return false;\n    }\n\n    if (def->node_len > DEF_NODE_LEN) {\n      fprintf(stderr, \"def '%s' has too many nodes: %u\\n\", def->name, def->node_len);\n      return false;\n    }\n\n    // Reads root\n    def->root = *buf++;\n\n    // Reads rbag_buf\n    memcpy(def->rbag_buf, buf, 8*def->rbag_len);\n    buf += def->rbag_len * 2;\n\n    // Reads node_buf\n    memcpy(def->node_buf, buf, 8*def->node_len);\n    buf += def->node_len * 2;\n  }\n\n  return true;\n}\n\n// Debug Printing\n// --------------\n\nvoid put_u32(char* B, u32 val) {\n  for (int i = 0; i < 8; i++, val >>= 4) {\n    B[8-i-1] = \"0123456789ABCDEF\"[val & 0xF];\n  }\n}\n\nShow show_port(Port port) {\n  // NOTE: this is done like that because sprintf seems not to be working\n  Show s;\n  switch (get_tag(port)) {\n    case VAR: memcpy(s.x, \"VAR:\", 4); put_u32(s.x+4, get_val(port)); break;\n    case REF: memcpy(s.x, \"REF:\", 4); put_u32(s.x+4, get_val(port)); break;\n    case ERA: memcpy(s.x, \"ERA:________\", 12); break;\n    case NUM: memcpy(s.x, \"NUM:\", 4); put_u32(s.x+4, get_val(port)); break;\n    case CON: memcpy(s.x, \"CON:\", 4); put_u32(s.x+4, get_val(port)); break;\n    case DUP: memcpy(s.x, \"DUP:\", 4); put_u32(s.x+4, get_val(port)); break;\n    case OPR: memcpy(s.x, \"OPR:\", 4); put_u32(s.x+4, get_val(port)); break;\n    case SWI: memcpy(s.x, \"SWI:\", 4); put_u32(s.x+4, get_val(port)); break;\n  }\n  s.x[12] = '\\0';\n  return s;\n}\n\nShow show_rule(Rule rule) {\n  Show s;\n  switch (rule) {\n    case LINK: memcpy(s.x, \"LINK\", 4); break;\n    case VOID: memcpy(s.x, \"VOID\", 4); break;\n    case ERAS: memcpy(s.x, \"ERAS\", 4); break;\n    case ANNI: memcpy(s.x, \"ANNI\", 4); break;\n    case COMM: memcpy(s.x, \"COMM\", 4); break;\n    case OPER: memcpy(s.x, \"OPER\", 4); break;\n    case SWIT: memcpy(s.x, \"SWIT\", 4); break;\n    case CALL: memcpy(s.x, \"CALL\", 4); break;\n    default  : memcpy(s.x, \"????\", 4); break;\n  }\n  s.x[4] = '\\0';\n  return s;\n}\n\n//void print_rbag(RBag* rbag) {\n  //printf(\"RBAG | FST-TREE     | SND-TREE    \\n\");\n  //printf(\"---- | ------------ | ------------\\n\");\n  //for (u32 i = rbag->lo_ini; i < rbag->lo_end; ++i) {\n    //Pair redex = rbag->lo_buf[i%RLEN];\n    //printf(\"%04X | %s | %s\\n\", i, show_port(get_fst(redex)).x, show_port(get_snd(redex)).x);\n  //}\n  //for (u32 i = 0; i > rbag->hi_end; ++i) {\n    //Pair redex = rbag->hi_buf[i];\n    //printf(\"%04X | %s | %s\\n\", i, show_port(get_fst(redex)).x, show_port(get_snd(redex)).x);\n  //}\n  //printf(\"==== | ============ | ============\\n\");\n//}\n\nvoid print_net(Net* net) {\n  printf(\"NODE | PORT-1       | PORT-2      \\n\");\n  printf(\"---- | ------------ | ------------\\n\");\n  for (u32 i = 0; i < G_NODE_LEN; ++i) {\n    Pair node = node_load(net, i);\n    if (node != 0) {\n      printf(\"%04X | %s | %s\\n\", i, show_port(get_fst(node)).x, show_port(get_snd(node)).x);\n    }\n  }\n  printf(\"==== | ============ |\\n\");\n  printf(\"VARS | VALUE        |\\n\");\n  printf(\"---- | ------------ |\\n\");\n  for (u32 i = 0; i < G_VARS_LEN; ++i) {\n    Port var = vars_load(net,i);\n    if (var != 0) {\n      printf(\"%04X | %s |\\n\", i, show_port(vars_load(net,i)).x);\n    }\n  }\n  printf(\"==== | ============ |\\n\");\n}\n\nvoid pretty_print_numb(Numb word) {\n  switch (get_typ(word)) {\n    case TY_SYM: {\n      switch (get_sym(word)) {\n        // types\n        case TY_U24: printf(\"[u24]\"); break;\n        case TY_I24: printf(\"[i24]\"); break;\n        case TY_F24: printf(\"[f24]\"); break;\n        // operations\n        case OP_ADD: printf(\"[+]\"); break;\n        case OP_SUB: printf(\"[-]\"); break;\n        case FP_SUB: printf(\"[:-]\"); break;\n        case OP_MUL: printf(\"[*]\"); break;\n        case OP_DIV: printf(\"[/]\"); break;\n        case FP_DIV: printf(\"[:/]\"); break;\n        case OP_REM: printf(\"[%%]\"); break;\n        case FP_REM: printf(\"[:%%]\"); break;\n        case OP_EQ:  printf(\"[=]\"); break;\n        case OP_NEQ: printf(\"[!]\"); break;\n        case OP_LT:  printf(\"[<]\"); break;\n        case OP_GT:  printf(\"[>]\"); break;\n        case OP_AND: printf(\"[&]\"); break;\n        case OP_OR:  printf(\"[|]\"); break;\n        case OP_XOR: printf(\"[^]\"); break;\n        case OP_SHL: printf(\"[<<]\"); break;\n        case FP_SHL: printf(\"[:<<]\"); break;\n        case OP_SHR: printf(\"[>>]\"); break;\n        case FP_SHR: printf(\"[:>>]\"); break;\n        default:     printf(\"[?]\"); break;\n      }\n      break;\n    }\n    case TY_U24: {\n      printf(\"%u\", get_u24(word));\n      break;\n    }\n    case TY_I24: {\n      printf(\"%+d\", get_i24(word));\n      break;\n    }\n    case TY_F24: {\n      if (isinf(get_f24(word))) {\n        if (signbit(get_f24(word))) {\n          printf(\"-inf\");\n        } else {\n          printf(\"+inf\");\n        }\n      } else if (isnan(get_f24(word))) {\n        printf(\"+NaN\");\n      } else {\n        printf(\"%.7e\", get_f24(word));\n      }\n      break;\n    }\n    default: {\n      switch (get_typ(word)) {\n        case OP_ADD: printf(\"[+0x%07X]\", get_u24(word)); break;\n        case OP_SUB: printf(\"[-0x%07X]\", get_u24(word)); break;\n        case FP_SUB: printf(\"[:-0x%07X]\", get_u24(word)); break;\n        case OP_MUL: printf(\"[*0x%07X]\", get_u24(word)); break;\n        case OP_DIV: printf(\"[/0x%07X]\", get_u24(word)); break;\n        case FP_DIV: printf(\"[:/0x%07X]\", get_u24(word)); break;\n        case OP_REM: printf(\"[%%0x%07X]\", get_u24(word)); break;\n        case FP_REM: printf(\"[:%%0x%07X]\", get_u24(word)); break;\n        case OP_EQ:  printf(\"[=0x%07X]\", get_u24(word)); break;\n        case OP_NEQ: printf(\"[!0x%07X]\", get_u24(word)); break;\n        case OP_LT:  printf(\"[<0x%07X]\", get_u24(word)); break;\n        case OP_GT:  printf(\"[>0x%07X]\", get_u24(word)); break;\n        case OP_AND: printf(\"[&0x%07X]\", get_u24(word)); break;\n        case OP_OR:  printf(\"[|0x%07X]\", get_u24(word)); break;\n        case OP_XOR: printf(\"[^0x%07X]\", get_u24(word)); break;\n        case OP_SHL: printf(\"[<<0x%07X]\", get_u24(word)); break;\n        case FP_SHL: printf(\"[:<<0x%07X]\", get_u24(word)); break;\n        case OP_SHR: printf(\"[>>0x%07X]\", get_u24(word)); break;\n        case FP_SHR: printf(\"[:>>0x%07X]\", get_u24(word)); break;\n        default:     printf(\"[?0x%07X]\", get_u24(word)); break;\n      }\n      break;\n    }\n  }\n\n}\n\nvoid pretty_print_port(Net* net, Book* book, Port port) {\n  Port stack[4096];\n  stack[0] = port;\n  u32 len = 1;\n  u32 num = 0;\n  while (len > 0) {\n    Port cur = stack[--len];\n    switch (get_tag(cur)) {\n      case CON: {\n        Pair node = node_load(net,get_val(cur));\n        Port p2   = get_snd(node);\n        Port p1   = get_fst(node);\n        printf(\"(\");\n        stack[len++] = new_port(ERA, (u32)(')'));\n        stack[len++] = p2;\n        stack[len++] = new_port(ERA, (u32)(' '));\n        stack[len++] = p1;\n        break;\n      }\n      case ERA: {\n        if (get_val(cur) != 0) {\n          printf(\"%c\", (char)get_val(cur));\n        } else {\n          printf(\"*\");\n        }\n        break;\n      }\n      case VAR: {\n        Port got = vars_load(net, get_val(cur));\n        if (got != NONE) {\n          stack[len++] = got;\n        } else {\n          printf(\"x%x\", get_val(cur));\n        }\n        break;\n      }\n      case NUM: {\n        pretty_print_numb(get_val(cur));\n        break;\n      }\n      case DUP: {\n        Pair node = node_load(net,get_val(cur));\n        Port p2   = get_snd(node);\n        Port p1   = get_fst(node);\n        printf(\"{\");\n        stack[len++] = new_port(ERA, (u32)('}'));\n        stack[len++] = p2;\n        stack[len++] = new_port(ERA, (u32)(' '));\n        stack[len++] = p1;\n        break;\n      }\n      case OPR: {\n        Pair node = node_load(net,get_val(cur));\n        Port p2   = get_snd(node);\n        Port p1   = get_fst(node);\n        printf(\"$(\");\n        stack[len++] = new_port(ERA, (u32)(')'));\n        stack[len++] = p2;\n        stack[len++] = new_port(ERA, (u32)(' '));\n        stack[len++] = p1;\n        break;\n      }\n      case SWI: {\n        Pair node = node_load(net,get_val(cur));\n        Port p2   = get_snd(node);\n        Port p1   = get_fst(node);\n        printf(\"?(\");\n        stack[len++] = new_port(ERA, (u32)(')'));\n        stack[len++] = p2;\n        stack[len++] = new_port(ERA, (u32)(' '));\n        stack[len++] = p1;\n        break;\n      }\n      case REF: {\n        u32  fid = get_val(cur) & 0xFFFFFFF;\n        Def* def = &book->defs_buf[fid];\n        printf(\"@%s\", def->name);\n        break;\n      }\n    }\n  }\n}\n\n//void pretty_print_rbag(Net* net, RBag* rbag) {\n  //for (u32 i = rbag->lo_ini; i < rbag->lo_end; ++i) {\n    //Pair redex = rbag->lo_buf[i];\n    //if (redex != 0) {\n      //pretty_print_port(net, get_fst(redex));\n      //printf(\" ~ \");\n      //pretty_print_port(net, get_snd(redex));\n      //printf(\"\\n\");\n    //}\n  //}\n  //for (u32 i = 0; i > rbag->hi_end; ++i) {\n    //Pair redex = rbag->hi_buf[i];\n    //if (redex != 0) {\n      //pretty_print_port(net, get_fst(redex));\n      //printf(\" ~ \");\n      //pretty_print_port(net, get_snd(redex));\n      //printf(\"\\n\");\n    //}\n  //}\n//}\n\n// Demos\n// -----\n\n  // stress_test 2^10 x 65536\n  //static const u8 BOOK_BUF[] = {6, 0, 0, 0, 0, 0, 0, 0, 109, 97, 105, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 4, 0, 0, 0, 11, 10, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 102, 117, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 25, 0, 0, 0, 2, 0, 0, 0, 102, 117, 110, 95, 95, 67, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 4, 0, 0, 0, 11, 0, 0, 1, 0, 0, 0, 0, 3, 0, 0, 0, 102, 117, 110, 95, 95, 67, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 6, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 9, 0, 0, 128, 20, 0, 0, 0, 9, 0, 0, 128, 44, 0, 0, 0, 13, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 30, 0, 0, 0, 3, 4, 0, 0, 38, 0, 0, 0, 24, 0, 0, 0, 16, 0, 0, 0, 8, 0, 0, 0, 24, 0, 0, 0, 4, 0, 0, 0, 108, 111, 111, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 41, 0, 0, 0, 5, 0, 0, 0, 108, 111, 111, 112, 95, 95, 67, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 0};\n\n  // stress_test 2^18 x 65536\n  //static const u8 BOOK_BUF[] = {6, 0, 0, 0, 0, 0, 0, 0, 109, 97, 105, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 4, 0, 0, 0, 11, 18, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 102, 117, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 25, 0, 0, 0, 2, 0, 0, 0, 102, 117, 110, 95, 95, 67, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 4, 0, 0, 0, 11, 0, 0, 1, 0, 0, 0, 0, 3, 0, 0, 0, 102, 117, 110, 95, 95, 67, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 6, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 9, 0, 0, 128, 20, 0, 0, 0, 9, 0, 0, 128, 44, 0, 0, 0, 13, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 30, 0, 0, 0, 3, 4, 0, 0, 38, 0, 0, 0, 24, 0, 0, 0, 16, 0, 0, 0, 8, 0, 0, 0, 24, 0, 0, 0, 4, 0, 0, 0, 108, 111, 111, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 41, 0, 0, 0, 5, 0, 0, 0, 108, 111, 111, 112, 95, 95, 67, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 0};\n\n  // bitonic_sort 2^20\n  //static const u8 BOOK_BUF[] = {19, 0, 0, 0, 0, 0, 0, 0, 109, 97, 105, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 89, 0, 0, 0, 4, 0, 0, 0, 11, 18, 0, 0, 12, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 100, 111, 119, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 15, 0, 0, 0, 60, 0, 0, 0, 20, 0, 0, 0, 44, 0, 0, 0, 28, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 0, 36, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 52, 0, 0, 0, 16, 0, 0, 0, 24, 0, 0, 0, 16, 0, 0, 0, 68, 0, 0, 0, 8, 0, 0, 0, 24, 0, 0, 0, 2, 0, 0, 0, 100, 111, 119, 110, 95, 95, 67, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 13, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 25, 0, 0, 128, 60, 0, 0, 0, 25, 0, 0, 128, 84, 0, 0, 0, 13, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 28, 0, 0, 0, 36, 0, 0, 0, 16, 0, 0, 0, 24, 0, 0, 0, 45, 0, 0, 0, 52, 0, 0, 0, 32, 0, 0, 0, 40, 0, 0, 0, 48, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 32, 0, 0, 0, 76, 0, 0, 0, 16, 0, 0, 0, 48, 0, 0, 0, 8, 0, 0, 0, 92, 0, 0, 0, 40, 0, 0, 0, 100, 0, 0, 0, 24, 0, 0, 0, 56, 0, 0, 0, 3, 0, 0, 0, 102, 108, 111, 119, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 15, 0, 0, 0, 60, 0, 0, 0, 20, 0, 0, 0, 44, 0, 0, 0, 28, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 36, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 52, 0, 0, 0, 16, 0, 0, 0, 24, 0, 0, 0, 16, 0, 0, 0, 68, 0, 0, 0, 8, 0, 0, 0, 24, 0, 0, 0, 4, 0, 0, 0, 102, 108, 111, 119, 95, 95, 67, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 14, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 9, 0, 0, 0, 60, 0, 0, 0, 129, 0, 0, 0, 84, 0, 0, 0, 13, 0, 0, 0, 28, 0, 0, 0, 22, 0, 0, 0, 8, 0, 0, 0, 35, 1, 0, 0, 0, 0, 0, 0, 36, 0, 0, 0, 44, 0, 0, 0, 16, 0, 0, 0, 24, 0, 0, 0, 53, 0, 0, 0, 48, 0, 0, 0, 32, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 32, 0, 0, 0, 76, 0, 0, 0, 56, 0, 0, 0, 48, 0, 0, 0, 8, 0, 0, 0, 92, 0, 0, 0, 40, 0, 0, 0, 100, 0, 0, 0, 16, 0, 0, 0, 108, 0, 0, 0, 24, 0, 0, 0, 56, 0, 0, 0, 5, 0, 0, 0, 103, 101, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 2, 0, 0, 0, 4, 0, 0, 0, 15, 0, 0, 0, 8, 0, 0, 0, 20, 0, 0, 0, 8, 0, 0, 0, 28, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 103, 101, 110, 95, 95, 67, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 12, 0, 0, 0, 6, 0, 0, 0, 4, 0, 0, 0, 41, 0, 0, 128, 68, 0, 0, 0, 41, 0, 0, 128, 84, 0, 0, 0, 13, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 29, 0, 0, 0, 60, 0, 0, 0, 38, 0, 0, 0, 54, 0, 0, 0, 59, 2, 0, 0, 46, 0, 0, 0, 35, 1, 0, 0, 16, 0, 0, 0, 59, 2, 0, 0, 24, 0, 0, 0, 32, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 76, 0, 0, 0, 16, 0, 0, 0, 32, 0, 0, 0, 8, 0, 0, 0, 92, 0, 0, 0, 24, 0, 0, 0, 40, 0, 0, 0, 7, 0, 0, 0, 109, 97, 105, 110, 95, 95, 67, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 4, 0, 0, 0, 11, 18, 0, 0, 12, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 109, 97, 105, 110, 95, 95, 67, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 73, 0, 0, 0, 4, 0, 0, 0, 11, 18, 0, 0, 12, 0, 0, 0, 11, 0, 0, 0, 20, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 115, 111, 114, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 15, 0, 0, 0, 60, 0, 0, 0, 20, 0, 0, 0, 44, 0, 0, 0, 28, 0, 0, 0, 81, 0, 0, 0, 0, 0, 0, 0, 36, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 52, 0, 0, 0, 16, 0, 0, 0, 24, 0, 0, 0, 16, 0, 0, 0, 68, 0, 0, 0, 8, 0, 0, 0, 24, 0, 0, 0, 10, 0, 0, 0, 115, 111, 114, 116, 95, 95, 67, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 17, 0, 0, 0, 9, 0, 0, 0, 4, 0, 0, 0, 25, 0, 0, 0, 60, 0, 0, 0, 73, 0, 0, 128, 92, 0, 0, 0, 73, 0, 0, 128, 116, 0, 0, 0, 13, 0, 0, 0, 36, 0, 0, 0, 22, 0, 0, 0, 29, 0, 0, 0, 35, 1, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 16, 0, 0, 0, 44, 0, 0, 0, 52, 0, 0, 0, 24, 0, 0, 0, 32, 0, 0, 0, 40, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 40, 0, 0, 0, 76, 0, 0, 0, 84, 0, 0, 0, 48, 0, 0, 0, 56, 0, 0, 0, 64, 0, 0, 0, 8, 0, 0, 0, 100, 0, 0, 0, 11, 0, 0, 0, 108, 0, 0, 0, 24, 0, 0, 0, 56, 0, 0, 0, 16, 0, 0, 0, 124, 0, 0, 0, 11, 1, 0, 0, 132, 0, 0, 0, 32, 0, 0, 0, 64, 0, 0, 0, 11, 0, 0, 0, 115, 117, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 2, 0, 0, 0, 4, 0, 0, 0, 15, 0, 0, 0, 8, 0, 0, 0, 20, 0, 0, 0, 8, 0, 0, 0, 28, 0, 0, 0, 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 115, 117, 109, 95, 95, 67, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 10, 0, 0, 0, 6, 0, 0, 0, 4, 0, 0, 0, 89, 0, 0, 128, 36, 0, 0, 0, 89, 0, 0, 128, 68, 0, 0, 0, 13, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 28, 0, 0, 0, 32, 0, 0, 0, 16, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 44, 0, 0, 0, 16, 0, 0, 0, 54, 0, 0, 0, 3, 4, 0, 0, 62, 0, 0, 0, 40, 0, 0, 0, 32, 0, 0, 0, 8, 0, 0, 0, 76, 0, 0, 0, 24, 0, 0, 0, 40, 0, 0, 0, 13, 0, 0, 0, 115, 119, 97, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 15, 0, 0, 0, 44, 0, 0, 0, 20, 0, 0, 0, 28, 0, 0, 0, 113, 0, 0, 0, 121, 0, 0, 0, 0, 0, 0, 0, 36, 0, 0, 0, 8, 0, 0, 0, 16, 0, 0, 0, 8, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 14, 0, 0, 0, 115, 119, 97, 112, 95, 95, 67, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 2, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 8, 0, 0, 0, 20, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 115, 119, 97, 112, 95, 95, 67, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 2, 0, 0, 0, 4, 0, 0, 0, 2, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 8, 0, 0, 0, 28, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 16, 0, 0, 0, 119, 97, 114, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 15, 0, 0, 0, 52, 0, 0, 0, 20, 0, 0, 0, 28, 0, 0, 0, 137, 0, 0, 0, 145, 0, 0, 0, 0, 0, 0, 0, 36, 0, 0, 0, 8, 0, 0, 0, 44, 0, 0, 0, 16, 0, 0, 0, 24, 0, 0, 0, 16, 0, 0, 0, 60, 0, 0, 0, 8, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 17, 0, 0, 0, 119, 97, 114, 112, 95, 95, 67, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 12, 0, 0, 0, 6, 0, 0, 0, 4, 0, 0, 0, 105, 0, 0, 0, 76, 0, 0, 0, 13, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 29, 0, 0, 0, 52, 0, 0, 0, 38, 0, 0, 0, 24, 0, 0, 0, 3, 15, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 62, 0, 0, 0, 40, 0, 0, 0, 3, 18, 0, 0, 70, 0, 0, 0, 16, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 84, 0, 0, 0, 24, 0, 0, 0, 92, 0, 0, 0, 8, 0, 0, 0, 40, 0, 0, 0, 18, 0, 0, 0, 119, 97, 114, 112, 95, 95, 67, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 21, 0, 0, 0, 12, 0, 0, 0, 4, 0, 0, 0, 129, 0, 0, 128, 92, 0, 0, 0, 129, 0, 0, 128, 132, 0, 0, 0, 13, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 28, 0, 0, 0, 36, 0, 0, 0, 16, 0, 0, 0, 24, 0, 0, 0, 44, 0, 0, 0, 52, 0, 0, 0, 32, 0, 0, 0, 40, 0, 0, 0, 61, 0, 0, 0, 68, 0, 0, 0, 48, 0, 0, 0, 56, 0, 0, 0, 76, 0, 0, 0, 84, 0, 0, 0, 64, 0, 0, 0, 72, 0, 0, 0, 80, 0, 0, 0, 88, 0, 0, 0, 8, 0, 0, 0, 100, 0, 0, 0, 56, 0, 0, 0, 108, 0, 0, 0, 40, 0, 0, 0, 116, 0, 0, 0, 24, 0, 0, 0, 124, 0, 0, 0, 72, 0, 0, 0, 88, 0, 0, 0, 0, 0, 0, 0, 140, 0, 0, 0, 48, 0, 0, 0, 148, 0, 0, 0, 32, 0, 0, 0, 156, 0, 0, 0, 16, 0, 0, 0, 164, 0, 0, 0, 64, 0, 0, 0, 80, 0, 0, 0};\n\n//COMPILED_BOOK_BUF//\n\n#ifdef IO\nvoid do_run_io(Net* net, Book* book, Port port);\n#endif\n\n// Main\n// ----\n\nvoid hvm_c(u32* book_buffer) {\n  // Creates static TMs\n  alloc_static_tms();\n\n  // Loads the Book\n  Book* book = NULL;\n  if (book_buffer) {\n    book = (Book*)malloc(sizeof(Book));\n    if (!book_load(book, book_buffer)) {\n      fprintf(stderr, \"failed to load book\\n\");\n\n      return;\n    }\n  }\n\n  // GMem\n  Net *net = net_new();\n\n  // Starts the timer\n  u64 start = time64();\n\n  // Creates an initial redex that calls main\n  boot_redex(net, new_pair(new_port(REF, 0), ROOT));\n\n  #ifdef IO\n  do_run_io(net, book, ROOT);\n  #else\n  normalize(net, book);\n  #endif\n\n  // Prints the result\n  printf(\"Result: \");\n  pretty_print_port(net, book, enter(net, ROOT));\n  printf(\"\\n\");\n\n  // Stops the timer\n  double duration = (time64() - start) / 1000000000.0; // seconds\n\n  // Prints interactions and time\n  u64 itrs = atomic_load(&net->itrs);\n  printf(\"- ITRS: %\" PRIu64 \"\\n\", itrs);\n  printf(\"- TIME: %.2fs\\n\", duration);\n  printf(\"- MIPS: %.2f\\n\", (double)itrs / duration / 1000000.0);\n\n  // Frees everything\n  free_static_tms();\n  free(net);\n  free(book);\n}\n\n#ifdef WITH_MAIN\nint main() {\n  hvm_c((u32*)BOOK_BUF);\n  return 0;\n}\n#endif\n"
  },
  {
    "path": "src/hvm.cu",
    "content": "#define INTERPRETED\n#define WITHOUT_MAIN\n\n#ifdef DEBUG\n  #define debug(...) printf(__VA_ARGS__)\n#else\n  #define debug(...)\n#endif\n\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n// Integers\n// --------\n\ntypedef  uint8_t u8;\ntypedef uint16_t u16;\ntypedef uint32_t u32;\ntypedef  int32_t i32;\ntypedef    float f32;\ntypedef   double f64;\ntypedef unsigned long long int u64;\n\n// Configuration\n// -------------\n\n// Clocks per Second\nconst u64 S = 2520000000;\n\n// Threads per Block\nconst u32 TPB_L2 = 7;\nconst u32 TPB    = 1 << TPB_L2;\n\n// Blocks per GPU\nconst u32 BPG_L2 = 7;\nconst u32 BPG    = 1 << BPG_L2;\n\n// Threads per GPU\nconst u32 TPG = TPB * BPG;\n\n//#define ALLOC_MODE SHARED\n//#define ALLOC_MODE GLOBAL\n\n// Types\n// -----\n\n// Local Types\ntypedef u8  Tag;  // Tag  ::= 3-bit (rounded up to u8)\ntypedef u32 Val;  // Val  ::= 29-bit (rounded up to u32)\ntypedef u32 Port; // Port ::= Tag + Val (fits a u32)\ntypedef u64 Pair; // Pair ::= Port + Port (fits a u64)\n\n// Rules\ntypedef u8 Rule; // Rule ::= 3-bit (rounded up to 8)\n\n// Numbs\ntypedef u32 Numb; // Numb ::= 29-bit (rounded up to u32)\n\n// Tags\nconst Tag VAR = 0x0; // variable\nconst Tag REF = 0x1; // reference\nconst Tag ERA = 0x2; // eraser\nconst Tag NUM = 0x3; // number\nconst Tag CON = 0x4; // constructor\nconst Tag DUP = 0x5; // duplicator\nconst Tag OPR = 0x6; // operator\nconst Tag SWI = 0x7; // switch\n\n// Interaction Rule Values\nconst Rule LINK = 0x0;\nconst Rule CALL = 0x1;\nconst Rule VOID = 0x2;\nconst Rule ERAS = 0x3;\nconst Rule ANNI = 0x4;\nconst Rule COMM = 0x5;\nconst Rule OPER = 0x6;\nconst Rule SWIT = 0x7;\n\n// Constants\nconst Port FREE = 0x00000000;\nconst Port ROOT = 0xFFFFFFF8;\nconst Port NONE = 0xFFFFFFFF;\n\n// Numbers\nconst Tag TY_SYM = 0x00;\nconst Tag TY_U24 = 0x01;\nconst Tag TY_I24 = 0x02;\nconst Tag TY_F24 = 0x03;\nconst Tag OP_ADD = 0x04;\nconst Tag OP_SUB = 0x05;\nconst Tag FP_SUB = 0x06;\nconst Tag OP_MUL = 0x07;\nconst Tag OP_DIV = 0x08;\nconst Tag FP_DIV = 0x09;\nconst Tag OP_REM = 0x0A;\nconst Tag FP_REM = 0x0B;\nconst Tag OP_EQ  = 0x0C;\nconst Tag OP_NEQ = 0x0D;\nconst Tag OP_LT  = 0x0E;\nconst Tag OP_GT  = 0x0F;\nconst Tag OP_AND = 0x10;\nconst Tag OP_OR  = 0x11;\nconst Tag OP_XOR = 0x12;\nconst Tag OP_SHL = 0x13;\nconst Tag FP_SHL = 0x14;\nconst Tag OP_SHR = 0x15;\nconst Tag FP_SHR = 0x16;\n\n// Evaluation Modes\nconst u8 SEED = 0;\nconst u8 GROW = 1;\nconst u8 WORK = 2;\n\n// Thread Redex Bag Length\nconst u32 RLEN = 256;\n\n// Thread Redex Bag\n// It uses the same space to store two stacks:\n// - HI: a high-priotity stack, for shrinking reductions\n// - LO: a low-priority stack, for growing reductions\nstruct RBag {\n  u32  hi_end;\n  Pair hi_buf[RLEN];\n  u32  lo_end;\n  Pair lo_buf[RLEN];\n};\n\n// Local Net\nconst u32 L_NODE_LEN = 0x2000;\nconst u32 L_VARS_LEN = 0x2000;\nstruct LNet {\n  Pair node_buf[L_NODE_LEN];\n  Port vars_buf[L_VARS_LEN];\n};\n\n// Global Net\nconst u32 G_NODE_LEN = 1 << 29; // max 536m nodes\nconst u32 G_VARS_LEN = 1 << 29; // max 536m vars\nconst u32 G_RBAG_LEN = TPB * BPG * RLEN * 3; // max 4m redexes\nstruct GNet {\n  u32  rbag_use_A; // total rbag redex count (buffer A)\n  u32  rbag_use_B; // total rbag redex count (buffer B)\n  Pair rbag_buf_A[G_RBAG_LEN]; // global redex bag (buffer A)\n  Pair rbag_buf_B[G_RBAG_LEN]; // global redex bag (buffer B)\n  Pair node_buf[G_NODE_LEN]; // global node buffer\n  Port vars_buf[G_VARS_LEN]; // global vars buffer\n  u32  node_put[TPB*BPG];\n  u32  vars_put[TPB*BPG];\n  u32  rbag_pos[TPB*BPG];\n  u8   mode; // evaluation mode (curr)\n  u64  itrs; // interaction count\n  u64  iadd; // interaction count adder\n  u64  leak; // leak count\n  u32  turn; // turn count\n  u8   down; // are we recursing down?\n  u8   rdec; // decrease rpos by 1?\n};\n\n// View Net: includes both GNet and LNet\nstruct Net {\n  i32   l_node_dif; // delta node space\n  i32   l_vars_dif; // delta vars space\n  Pair *l_node_buf; // local node buffer values\n  Port *l_vars_buf; // local vars buffer values\n  u32  *g_rbag_use_A; // global rbag count (active buffer)\n  u32  *g_rbag_use_B; // global rbag count (inactive buffer)\n  Pair *g_rbag_buf_A; // global rbag values (active buffer)\n  Pair *g_rbag_buf_B; // global rbag values (inactive buffer)\n  Pair *g_node_buf; // global node buffer values\n  Port *g_vars_buf; // global vars buffer values\n  u32  *g_node_put; // next global node allocation index\n  u32  *g_vars_put; // next global vars allocation index\n};\n\n// Thread Memory\nstruct TM {\n  u32  page; // page index\n  u32  nput; // node alloc index\n  u32  vput; // vars alloc index\n  u32  mode; // evaluation mode\n  u32  itrs; // interactions\n  u32  leak; // leaks\n  u32  nloc[L_NODE_LEN/TPB]; // node allocs\n  u32  vloc[L_NODE_LEN/TPB]; // vars allocs\n  RBag rbag; // tmem redex bag\n};\n\n// Top-Level Definition\nstruct Def {\n  char name[256];\n  bool safe;\n  u32  rbag_len;\n  u32  node_len;\n  u32  vars_len;\n  Port root;\n  Pair rbag_buf[L_NODE_LEN/TPB];\n  Pair node_buf[L_NODE_LEN/TPB];\n};\n\ntypedef struct Book Book;\n\n// A Foreign Function\ntypedef struct {\n  char name[256];\n  Port (*func)(GNet*, Port);\n} FFn;\n\n// Book of Definitions\nstruct Book {\n  u32 defs_len;\n  Def defs_buf[0x4000];\n  u32 ffns_len;\n  FFn ffns_buf[0x4000];\n};\n\n// Static Book\n__device__ Book BOOK;\n\n// Debugger\n// --------\n\nstruct Show {\n  char x[13];\n};\n\n__device__ __host__ void put_u16(char* B, u16 val);\n__device__ __host__ Show show_port(Port port);\n__device__ Show show_rule(Rule rule);\n__device__ void print_rbag(Net* net, TM* tm);\n__device__ __host__ void print_net(Net* net, u32, u32);\n__device__ void pretty_print_numb(Numb word);\n__device__ void pretty_print_port(Net* net, Port port);\n__device__ void pretty_print_rbag(Net* net, RBag* rbag);\n__global__ void print_heatmap(GNet* gnet, u32 turn);\n\n// Utils\n// -----\n\n__device__ __host__ f32 clamp(f32 x, f32 min, f32 max) {\n  const f32 t = x < min ? min : x;\n  return (t > max) ? max : t;\n}\n\n// TODO: write a time64() function that returns the time as fast as possible as a u64\nstatic inline u64 time64() {\n  struct timespec ts;\n  clock_gettime(CLOCK_MONOTONIC, &ts);\n  return (u64)ts.tv_sec * 1000000000ULL + (u64)ts.tv_nsec;\n}\n\n__device__ inline u32 TID() {\n  return threadIdx.x;\n}\n\n__device__ inline u32 BID() {\n  return blockIdx.x;\n}\n\n__device__ inline u32 GID() {\n  return TID() + BID() * blockDim.x;\n}\n\n__device__ __host__ inline u32 div(u32 a, u32 b) {\n  return (a + b - 1) / b;\n}\n\n__device__ u32 push_index(u32 msk, u32 idx) {\n  return msk | (1U << (31 - idx));\n}\n\n__device__ u32 pop_index(u32* msk) {\n  u32 idx = __clz(*msk);\n  *msk &= ~(1U << (31 - idx));\n  return idx;\n}\n\n// Port: Constructor and Getters\n// -----------------------------\n\n__device__ __host__ inline Port new_port(Tag tag, Val val) {\n  return (val << 3) | tag;\n}\n\n__device__ __host__ inline Tag get_tag(Port port) {\n  return port & 7;\n}\n\n__device__ __host__ inline Val get_val(Port port) {\n  return port >> 3;\n}\n\n// Pair: Constructor and Getters\n// -----------------------------\n\n__device__ __host__ inline Pair new_pair(Port fst, Port snd) {\n  return ((u64)snd << 32) | fst;\n}\n\n__device__ __host__ inline Port get_fst(Pair pair) {\n  return pair & 0xFFFFFFFF;\n}\n\n__device__ __host__ inline Port get_snd(Pair pair) {\n  return pair >> 32;\n}\n\n__device__ __host__ Pair set_par_flag(Pair pair) {\n  Port p1 = get_fst(pair);\n  Port p2 = get_snd(pair);\n  if (get_tag(p1) == REF) {\n    return new_pair(new_port(get_tag(p1), get_val(p1) | 0x10000000), p2);\n  } else {\n    return pair;\n  }\n}\n\n__device__ __host__ Pair clr_par_flag(Pair pair) {\n  Port p1 = get_fst(pair);\n  Port p2 = get_snd(pair);\n  if (get_tag(p1) == REF) {\n    return new_pair(new_port(get_tag(p1), get_val(p1) & 0xFFFFFFF), p2);\n  } else {\n    return pair;\n  }\n}\n\n__device__ __host__ bool get_par_flag(Pair pair) {\n  Port p1 = get_fst(pair);\n  if (get_tag(p1) == REF) {\n    return (get_val(p1) >> 28) == 1;\n  } else {\n    return false;\n  }\n}\n\n// Utils\n// -----\n\n// Swaps two ports.\n__device__ __host__ inline void swap(Port *a, Port *b) {\n  Port x = *a; *a = *b; *b = x;\n}\n\n// Transposes an index over a matrix.\n__device__ u32 transpose(u32 idx, u32 width, u32 height) {\n  u32 old_row = idx / width;\n  u32 old_col = idx % width;\n  u32 new_row = old_col % height;\n  u32 new_col = old_col / height + old_row * (width / height);\n  return new_row * width + new_col;\n}\n\n// Returns true if all 'x' are true, block-wise\n__device__ __noinline__ bool block_all(bool x) {\n  __shared__ bool res;\n  if (TID() == 0) res = true;\n  __syncthreads();\n  if (!x) res = false;\n  __syncthreads();\n  return res;\n}\n\n// Returns true if any 'x' is true, block-wise\n__device__ __noinline__ bool block_any(bool x) {\n  __shared__ bool res;\n  if (TID() == 0) res = false;\n  __syncthreads();\n  if (x) res = true;\n  __syncthreads();\n  return res;\n}\n\n// Returns the sum of a value, block-wise\ntemplate <typename A>\n__device__ __noinline__ A block_sum(A x) {\n  __shared__ A res;\n  if (TID() == 0) res = 0;\n  __syncthreads();\n  atomicAdd(&res, x);\n  __syncthreads();\n  return res;\n}\n\n// Returns the sum of a boolean, block-wise\n__device__ __noinline__ u32 block_count(bool x) {\n  __shared__ u32 res;\n  if (TID() == 0) res = 0;\n  __syncthreads();\n  atomicAdd(&res, x);\n  __syncthreads();\n  return res;\n}\n\n// Prints a 4-bit value for each thread in a block\n__device__ void block_print(u32 x) {\n  __shared__ u8 value[TPB];\n\n  value[TID()] = x;\n  __syncthreads();\n\n  if (TID() == 0) {\n    for (u32 i = 0; i < TPB; ++i) {\n      printf(\"%x\", min(value[i],0xF));\n    }\n  }\n  __syncthreads();\n}\n\n// Ports / Pairs / Rules\n// ---------------------\n\n// True if this port has a pointer to a node.\n__device__ __host__ inline bool is_nod(Port a) {\n  return get_tag(a) >= CON;\n}\n\n// True if this port is a variable.\n__device__ __host__ inline bool is_var(Port a) {\n  return get_tag(a) == VAR;\n}\n\n// True if this port is a local node/var (that can leak).\n__device__ __host__ inline bool is_local(Port a) {\n  return (is_nod(a) || is_var(a)) && get_val(a) < L_NODE_LEN;\n}\n\n// True if this port is a global node/var (that can be leaked into).\n__device__ __host__ inline bool is_global(Port a) {\n  return (is_nod(a) || is_var(a)) && get_val(a) >= L_NODE_LEN;\n}\n\n// Given two tags, gets their interaction rule. Uses a u64mask lookup table.\n__device__ __host__ inline Rule get_rule(Port A, Port B) {\n  const u64 x = 0b0111111010110110110111101110111010110000111100001111000100000010;\n  const u64 y = 0b0000110000001100000011100000110011111110111111100010111000000000;\n  const u64 z = 0b1111100011111000111100001111000011000000000000000000000000000000;\n  const u64 i = ((u64)get_tag(A) << 3) | (u64)get_tag(B);\n  return (Rule)((x>>i&1) | (y>>i&1)<<1 | (z>>i&1)<<2);\n}\n\n// Same as above, but receiving a pair.\n__device__ __host__ inline Rule get_pair_rule(Pair AB) {\n  return get_rule(get_fst(AB), get_snd(AB));\n}\n\n// Should we swap ports A and B before reducing this rule?\n__device__ __host__ inline bool should_swap(Port A, Port B) {\n  return get_tag(B) < get_tag(A);\n}\n// Gets a rule's priority\n__device__ __host__ inline bool is_high_priority(Rule rule) {\n  return (bool)((0b00011101 >> rule) & 1);\n}\n\n// Adjusts a newly allocated port.\n__device__ inline Port adjust_port(Net* net, TM* tm, Port port) {\n  Tag tag = get_tag(port);\n  Val val = get_val(port);\n  if (is_nod(port)) return new_port(tag, tm->nloc[val]);\n  if (is_var(port)) return new_port(tag, tm->vloc[val]);\n  return new_port(tag, val);\n}\n\n// Adjusts a newly allocated pair.\n__device__ inline Pair adjust_pair(Net* net, TM* tm, Pair pair) {\n  Port p1 = adjust_port(net, tm, get_fst(pair));\n  Port p2 = adjust_port(net, tm, get_snd(pair));\n  return new_pair(p1, p2);\n}\n\n// Words\n// -----\n\n// Constructor and getters for SYM (operation selector)\n__device__ __host__ inline Numb new_sym(u32 val) {\n  return (val << 5) | TY_SYM;\n}\n\n__device__ __host__ inline u32 get_sym(Numb word) {\n  return (word >> 5);\n}\n\n// Constructor and getters for U24 (unsigned 24-bit integer)\n__device__ __host__ inline Numb new_u24(u32 val) {\n  return (val << 5) | TY_U24;\n}\n\n__device__ __host__ inline u32 get_u24(Numb word) {\n  return word >> 5;\n}\n\n// Constructor and getters for I24 (signed 24-bit integer)\n__device__ __host__ inline Numb new_i24(i32 val) {\n  return ((u32)val << 5) | TY_I24;\n}\n\n__device__ __host__ inline i32 get_i24(Numb word) {\n  return ((i32)word) << 3 >> 8;\n}\n\n// Constructor and getters for F24 (24-bit float)\n__device__ __host__ inline Numb new_f24(f32 val) {\n  u32 bits = *(u32*)&val;\n  u32 shifted_bits = bits >> 8;\n  u32 lost_bits = bits & 0xFF;\n  // round ties to even\n  shifted_bits += (!isnan(val)) & ((lost_bits - ((lost_bits >> 7) & !shifted_bits)) >> 7);\n  // ensure NaNs don't become infinities\n  shifted_bits |= isnan(val);\n  return (shifted_bits << 5) | TY_F24;\n}\n\n__device__ __host__ inline f32 get_f24(Numb word) {\n  u32 bits = (word << 3) & 0xFFFFFF00;\n  return *(f32*)&bits;\n}\n\n// Flip flag\n__device__ __host__ inline Tag get_typ(Numb word) {\n  return word & 0x1F;\n}\n\n__device__ __host__ inline bool is_num(Numb word) {\n  return get_typ(word) >= TY_U24 && get_typ(word) <= TY_F24;\n}\n\n__device__ __host__ inline bool is_cast(Numb word) {\n  return get_typ(word) == TY_SYM && get_sym(word) >= TY_U24 && get_sym(word) <= TY_F24;\n}\n\n// Cast a number to another type.\n// The semantics are meant to spiritually resemble rust's numeric casts:\n// - i24 <-> u24: is just reinterpretation of bits\n// - f24  -> i24,\n//   f24  -> u24: casts to the \"closest\" integer representing this float,\n//                saturating if out of range and 0 if NaN\n// - i24  -> f24,\n//   u24  -> f24: casts to the \"closest\" float representing this integer.\n__device__ __host__ inline Numb cast(Numb a, Numb b) {\n  if (get_sym(a) == TY_U24 && get_typ(b) == TY_U24) return b;\n  if (get_sym(a) == TY_U24 && get_typ(b) == TY_I24) {\n    // reinterpret bits\n    i32 val = get_i24(b);\n    return new_u24(*(u32*) &val);\n  }\n  if (get_sym(a) == TY_U24 && get_typ(b) == TY_F24) {\n    f32 val = get_f24(b);\n    if (isnan(val)) {\n      return new_u24(0);\n    }\n    return new_u24((u32) clamp(val, 0.0, 16777215));\n  }\n\n  if (get_sym(a) == TY_I24 && get_typ(b) == TY_U24) {\n    // reinterpret bits\n    u32 val = get_u24(b);\n    return new_i24(*(i32*) &val);\n  }\n  if (get_sym(a) == TY_I24 && get_typ(b) == TY_I24) return b;\n  if (get_sym(a) == TY_I24 && get_typ(b) == TY_F24) {\n    f32 val = get_f24(b);\n    if (isnan(val)) {\n      return new_i24(0);\n    }\n    return new_i24((i32) clamp(val, -8388608.0, 8388607.0));\n  }\n\n  if (get_sym(a) == TY_F24 && get_typ(b) == TY_U24) return new_f24((f32) get_u24(b));\n  if (get_sym(a) == TY_F24 && get_typ(b) == TY_I24) return new_f24((f32) get_i24(b));\n  if (get_sym(a) == TY_F24 && get_typ(b) == TY_F24) return b;\n\n  return new_u24(0);\n}\n\n// Partial application\n__device__ __host__ inline Numb partial(Numb a, Numb b) {\n  return (b & ~0x1F) | get_sym(a);\n}\n\n// Operate function\n__device__ __host__ inline Numb operate(Numb a, Numb b) {\n  Tag at = get_typ(a);\n  Tag bt = get_typ(b);\n  if (at == TY_SYM && bt == TY_SYM) {\n    return new_u24(0);\n  }\n  if (is_cast(a) && is_num(b)) {\n    return cast(a, b);\n  }\n  if (is_cast(b) && is_num(a)) {\n    return cast(b, a);\n  }\n  if (at == TY_SYM && bt != TY_SYM) {\n    return partial(a, b);\n  }\n  if (at != TY_SYM && bt == TY_SYM) {\n    return partial(b, a);\n  }\n  if (at >= OP_ADD && bt >= OP_ADD) {\n    return new_u24(0);\n  }\n  if (at < OP_ADD && bt < OP_ADD) {\n    return new_u24(0);\n  }\n  Tag op, ty;\n  Numb swp;\n  if (at >= OP_ADD) {\n    op = at; ty = bt;\n  } else {\n    op = bt; ty = at; swp = a; a = b; b = swp;\n  }\n  switch (ty) {\n    case TY_U24: {\n      u32 av = get_u24(a);\n      u32 bv = get_u24(b);\n      switch (op) {\n        case OP_ADD: return new_u24(av + bv);\n        case OP_SUB: return new_u24(av - bv);\n        case FP_SUB: return new_u24(bv - av);\n        case OP_MUL: return new_u24(av * bv);\n        case OP_DIV: return new_u24(av / bv);\n        case FP_DIV: return new_u24(bv / av);\n        case OP_REM: return new_u24(av % bv);\n        case FP_REM: return new_u24(bv % av);\n        case OP_EQ:  return new_u24(av == bv);\n        case OP_NEQ: return new_u24(av != bv);\n        case OP_LT:  return new_u24(av < bv);\n        case OP_GT:  return new_u24(av > bv);\n        case OP_AND: return new_u24(av & bv);\n        case OP_OR:  return new_u24(av | bv);\n        case OP_XOR: return new_u24(av ^ bv);\n        case OP_SHL: return new_u24(av << (bv & 31));\n        case FP_SHL: return new_u24(bv << (av & 31));\n        case OP_SHR: return new_u24(av >> (bv & 31));\n        case FP_SHR: return new_u24(bv >> (av & 31));\n        default:     return new_u24(0);\n      }\n    }\n    case TY_I24: {\n      i32 av = get_i24(a);\n      i32 bv = get_i24(b);\n      switch (op) {\n        case OP_ADD: return new_i24(av + bv);\n        case OP_SUB: return new_i24(av - bv);\n        case FP_SUB: return new_i24(bv - av);\n        case OP_MUL: return new_i24(av * bv);\n        case OP_DIV: return new_i24(av / bv);\n        case FP_DIV: return new_i24(bv / av);\n        case OP_REM: return new_i24(av % bv);\n        case FP_REM: return new_i24(bv % av);\n        case OP_EQ:  return new_u24(av == bv);\n        case OP_NEQ: return new_u24(av != bv);\n        case OP_LT:  return new_u24(av < bv);\n        case OP_GT:  return new_u24(av > bv);\n        case OP_AND: return new_i24(av & bv);\n        case OP_OR:  return new_i24(av | bv);\n        case OP_XOR: return new_i24(av ^ bv);\n        default:     return new_i24(0);\n      }\n    }\n    case TY_F24: {\n      float av = get_f24(a);\n      float bv = get_f24(b);\n      switch (op) {\n        case OP_ADD: return new_f24(av + bv);\n        case OP_SUB: return new_f24(av - bv);\n        case FP_SUB: return new_f24(bv - av);\n        case OP_MUL: return new_f24(av * bv);\n        case OP_DIV: return new_f24(av / bv);\n        case FP_DIV: return new_f24(bv / av);\n        case OP_REM: return new_f24(fmodf(av, bv));\n        case FP_REM: return new_f24(fmodf(bv, av));\n        case OP_EQ:  return new_u24(av == bv);\n        case OP_NEQ: return new_u24(av != bv);\n        case OP_LT:  return new_u24(av < bv);\n        case OP_GT:  return new_u24(av > bv);\n        case OP_AND: return new_f24(atan2f(av, bv));\n        case OP_OR:  return new_f24(logf(bv) / logf(av));\n        case OP_XOR: return new_f24(powf(av, bv));\n        case OP_SHL: return new_f24(sin(av + bv));\n        case OP_SHR: return new_f24(tan(av + bv));\n        default:     return new_f24(0);\n      }\n    }\n    default: return new_u24(0);\n  }\n}\n\n// RBag\n// ----\n\n__device__ RBag rbag_new() {\n  RBag rbag;\n  rbag.hi_end = 0;\n  rbag.lo_end = 0;\n  return rbag;\n}\n\n__device__ u32 rbag_len(RBag* rbag) {\n  return rbag->hi_end + rbag->lo_end;\n}\n\n__device__ u32 rbag_has_highs(RBag* rbag) {\n  return rbag->hi_end > 0;\n}\n\n__device__ void push_redex(TM* tm, Pair redex) {\n  #ifdef DEBUG\n  bool free_hi = tm->rbag.hi_end < RLEN;\n  bool free_lo = tm->rbag.lo_end < RLEN;\n  if (!free_hi || !free_lo) {\n    debug(\"push_redex: limited resources, maybe corrupting memory\\n\");\n  }\n  #endif\n\n  Rule rule = get_pair_rule(redex);\n  if (is_high_priority(rule)) {\n    tm->rbag.hi_buf[tm->rbag.hi_end++] = redex;\n  } else {\n    tm->rbag.lo_buf[tm->rbag.lo_end++] = redex;\n  }\n}\n\n__device__ Pair pop_redex(TM* tm) {\n  if (tm->rbag.hi_end > 0) {\n    return tm->rbag.hi_buf[(--tm->rbag.hi_end) % RLEN];\n  } else if (tm->rbag.lo_end > 0) {\n    return tm->rbag.lo_buf[(--tm->rbag.lo_end) % RLEN];\n  } else {\n    return 0;\n  }\n}\n\n// TM\n// --\n\n__device__ TM tmem_new() {\n  TM tm;\n  tm.rbag = rbag_new();\n  tm.nput = 1;\n  tm.vput = 1;\n  tm.mode = SEED;\n  tm.itrs = 0;\n  tm.leak = 0;\n  return tm;\n}\n\n// Net\n// ----\n\n__device__ Net vnet_new(GNet* gnet, void* smem, u32 turn) {\n  Net net;\n  net.l_node_dif   = 0;\n  net.l_vars_dif   = 0;\n  net.l_node_buf   = smem == NULL ? net.l_node_buf : ((LNet*)smem)->node_buf;\n  net.l_vars_buf   = smem == NULL ? net.l_vars_buf : ((LNet*)smem)->vars_buf;\n  net.g_rbag_use_A = turn % 2 == 0 ? &gnet->rbag_use_A : &gnet->rbag_use_B;\n  net.g_rbag_use_B = turn % 2 == 0 ? &gnet->rbag_use_B : &gnet->rbag_use_A;\n  net.g_rbag_buf_A = turn % 2 == 0 ? gnet->rbag_buf_A : gnet->rbag_buf_B;\n  net.g_rbag_buf_B = turn % 2 == 0 ? gnet->rbag_buf_B : gnet->rbag_buf_A;\n  net.g_node_buf   = gnet->node_buf;\n  net.g_vars_buf   = gnet->vars_buf;\n  net.g_node_put   = &gnet->node_put[GID()];\n  net.g_vars_put   = &gnet->vars_put[GID()];\n  return net;\n}\n\n// Stores a new node on global.\n__device__ inline void node_create(Net* net, u32 loc, Pair val) {\n  Pair old;\n  if (loc < L_NODE_LEN) {\n    net->l_node_dif += 1;\n    old = atomicExch(&net->l_node_buf[loc], val);\n  } else {\n    old = atomicExch(&net->g_node_buf[loc], val);\n  }\n  #ifdef DEBUG\n  if (old != 0) printf(\"[%04x] ERR NODE_CREATE | %04x\\n\", GID(), loc);\n  #endif\n}\n\n// Stores a var on global.\n__device__ inline void vars_create(Net* net, u32 var, Port val) {\n  Port old;\n  if (var < L_VARS_LEN) {\n    net->l_vars_dif += 1;\n    old = atomicExch(&net->l_vars_buf[var], val);\n  } else {\n    old = atomicExch(&net->g_vars_buf[var], val);\n  }\n  #ifdef DEBUG\n  if (old != 0) printf(\"[%04x] ERR VARS_CREATE | %04x\\n\", GID(), var);\n  #endif\n}\n\n// Reads a node from global.\n__device__ __host__ inline Pair node_load(Net* net, u32 loc) {\n  Pair got;\n  if (loc < L_NODE_LEN) {\n    got = net->l_node_buf[loc];\n  } else {\n    got = net->g_node_buf[loc];\n  }\n  return got;\n}\n\n// Reads a var from global.\n__device__ __host__ inline Port vars_load(Net* net, u32 var) {\n  Port got;\n  if (var < L_VARS_LEN) {\n    got = net->l_vars_buf[var];\n  } else {\n    got = net->g_vars_buf[var];\n  }\n  return got;\n}\n\n// Exchanges a node on global by a value. Returns old.\n__device__ inline Pair node_exchange(Net* net, u32 loc, Pair val) {\n  Pair got = 0;\n  if (loc < L_NODE_LEN) {\n    got = atomicExch(&net->l_node_buf[loc], val);\n  } else {\n    got = atomicExch(&net->g_node_buf[loc], val);\n  }\n  #ifdef DEBUG\n  if (got == 0) printf(\"[%04x] ERR NODE_EXCHANGE | %04x\\n\", GID(), loc);\n  #endif\n  return got;\n}\n\n// Exchanges a var on global by a value. Returns old.\n__device__ inline Port vars_exchange(Net* net, u32 var, Port val) {\n  Port got = 0;\n  if (var < L_VARS_LEN) {\n    got = atomicExch(&net->l_vars_buf[var], val);\n  } else {\n    got = atomicExch(&net->g_vars_buf[var], val);\n  }\n  #ifdef DEBUG\n  if (got == 0) printf(\"[%04x] ERR VARS_EXCHANGE | %04x\\n\", GID(), var);\n  #endif\n  return got;\n}\n\n// Takes a node.\n__device__ inline Pair node_take(Net* net, u32 loc) {\n  Pair got = 0;\n  if (loc < L_NODE_LEN) {\n    net->l_node_dif -= 1;\n    got = atomicExch(&net->l_node_buf[loc], 0);\n  } else {\n    got = atomicExch(&net->g_node_buf[loc], 0);\n  }\n  #ifdef DEBUG\n  if (got == 0) printf(\"[%04x] ERR NODE_TAKE | %04x\\n\", GID(), loc);\n  #endif\n  return got;\n}\n\n// Takes a var.\n__device__ inline Port vars_take(Net* net, u32 var) {\n  Port got = 0;\n  if (var < L_VARS_LEN) {\n    net->l_vars_dif -= 1;\n    got = atomicExch(&net->l_vars_buf[var], 0);\n  } else {\n    got = atomicExch(&net->g_vars_buf[var], 0);\n  }\n  #ifdef DEBUG\n  if (got == 0) printf(\"[%04x] ERR VARS_TAKE | %04x\\n\", GID(), var);\n  #endif\n  return got;\n}\n\n// Allocator\n// ---------\n\ntemplate <typename A>\n__device__ u32 g_alloc_1(Net* net, u32* g_put, A* g_buf) {\n  u32 lps = 0;\n  while (true) {\n    u32 lc = GID()*(G_NODE_LEN/TPG) + (*g_put%(G_NODE_LEN/TPG));\n    A elem = g_buf[lc];\n    *g_put += 1;\n    if (lc >= L_NODE_LEN && elem == 0) {\n      return lc;\n    }\n    if (++lps >= G_NODE_LEN/TPG) printf(\"OOM\\n\"); // FIXME: remove\n    //assert(++lps < G_NODE_LEN/TPG); // FIXME: enable?\n  }\n}\n\ntemplate <typename A>\n__device__ u32 g_alloc(Net* net, u32* ret, u32* g_put, A* g_buf, u32 num) {\n  u32 got = 0;\n  u32 lps = 0;\n  while (got < num) {\n    u32 lc = GID()*(G_NODE_LEN/TPG) + (*g_put%(G_NODE_LEN/TPG));\n    A elem = g_buf[lc];\n    *g_put += 1;\n    if (lc >= L_NODE_LEN && elem == 0) {\n      ret[got++] = lc;\n    }\n\n    if (++lps >= G_NODE_LEN/TPG) printf(\"OOM\\n\"); // FIXME: remove\n    //assert(++lps < G_NODE_LEN/TPG); // FIXME: enable?\n  }\n  return got;\n\n}\n\ntemplate <typename A>\n__device__ u32 l_alloc(Net* net, u32* ret, u32* l_put, A* l_buf, u32 num) {\n  u32 got = 0;\n  u32 lps = 0;\n  while (got < num) {\n    u32 lc = ((*l_put)++ * TPB) % L_NODE_LEN + TID();\n    A elem = l_buf[lc];\n    if (++lps >= L_NODE_LEN/TPB) {\n      break;\n    }\n    if (lc > 0 && elem == 0) {\n      ret[got++] = lc;\n    }\n  }\n  return got;\n}\n\ntemplate <typename A>\n__device__ u32 l_alloc_1(Net* net, u32* ret, u32* l_put, A* l_buf, u32* lps) {\n  u32 got = 0;\n  while (true) {\n    u32 lc = ((*l_put)++ * TPB) % L_NODE_LEN + TID();\n    A elem = l_buf[lc];\n    if (++(*lps) >= L_NODE_LEN/TPB) {\n      break;\n    }\n    if (lc > 0 && elem == 0) {\n      return lc;\n    }\n  }\n  return got;\n}\n\n__device__ u32 g_node_alloc_1(Net* net) {\n  return g_alloc_1(net, net->g_node_put, net->g_node_buf);\n}\n\n__device__ u32 g_vars_alloc_1(Net* net) {\n  return g_alloc_1(net, net->g_vars_put, net->g_vars_buf);\n}\n\n__device__ u32 g_node_alloc(Net* net, TM* tm, u32 num) {\n  return g_alloc(net, tm->nloc, net->g_node_put, net->g_node_buf, num);\n}\n\n__device__ u32 g_vars_alloc(Net* net, TM* tm, u32 num) {\n  return g_alloc(net, tm->vloc, net->g_vars_put, net->g_vars_buf, num);\n}\n\n__device__ u32 l_node_alloc(Net* net, TM* tm, u32 num) {\n  return l_alloc(net, tm->nloc, &tm->nput, net->l_node_buf, num);\n}\n\n__device__ u32 l_vars_alloc(Net* net, TM* tm, u32 num) {\n  return l_alloc(net, tm->vloc, &tm->vput, net->l_vars_buf, num);\n}\n\n__device__ u32 l_node_alloc_1(Net* net, TM* tm, u32* lps) {\n  return l_alloc_1(net, tm->nloc, &tm->nput, net->l_node_buf, lps);\n}\n\n__device__ u32 l_vars_alloc_1(Net* net, TM* tm, u32* lps) {\n  return l_alloc_1(net, tm->vloc, &tm->vput, net->l_vars_buf, lps);\n}\n\n__device__ u32 node_alloc_1(Net* net, TM* tm, u32* lps) {\n  if (tm->mode != WORK) {\n    return g_node_alloc_1(net);\n  } else {\n    return l_node_alloc_1(net, tm, lps);\n  }\n}\n\n__device__ u32 vars_alloc_1(Net* net, TM* tm, u32* lps) {\n  if (tm->mode != WORK) {\n    return g_vars_alloc_1(net);\n  } else {\n    return l_vars_alloc_1(net, tm, lps);\n  }\n}\n\n// Linking\n// -------\n\n// Finds a variable's value.\n__device__ inline Port peek(Net* net, Port var) {\n  while (get_tag(var) == VAR) {\n    Port val = vars_load(net, get_val(var));\n    if (val == NONE) break;\n    if (val == 0) break;\n    var = val;\n  }\n  return var;\n}\n\n// Finds a variable's value.\n__device__ inline Port enter(Net* net, Port var) {\n  u32 lps = 0;\n  Port init = var;\n  // While `B` is VAR: extend it (as an optimization)\n  while (get_tag(var) == VAR) {\n    // Takes the current `var` substitution as `val`\n    Port val = vars_exchange(net, get_val(var), NONE);\n    // If there was no `val`, stop, as there is no extension\n    if (val == NONE) {\n      break;\n    }\n    // Sanity check: if global A is unfilled, stop\n    if (val == 0) {\n      break;\n    }\n    // Otherwise, delete `B` (we own both) and continue\n    vars_take(net, get_val(var));\n    //if (++lps > 65536) printf(\"[%04x] BUG A | init=%s var=%s val=%s\\n\", GID(), show_port(init).x, show_port(var).x, show_port(val).x);\n    var = val;\n  }\n  return var;\n}\n\n// Atomically Links `A ~ B`.\n__device__ void link(Net* net, TM* tm, Port A, Port B) {\n  Port INI_A = A;\n  Port INI_B = B;\n\n  u32 lps = 0;\n\n  // Attempts to directionally point `A ~> B`\n  while (true) {\n\n    // If `A` is NODE: swap `A` and `B`, and continue\n    if (get_tag(A) != VAR && get_tag(B) == VAR) {\n      Port X = A; A = B; B = X;\n    }\n\n    // If `A` is NODE: create the `A ~ B` redex\n    if (get_tag(A) != VAR) {\n      //printf(\"[%04x] new redex A %s ~ %s\\n\", GID(), show_port(A).x, show_port(B).x);\n      push_redex(tm, new_pair(A, B)); // TODO: move global ports to local\n      break;\n    }\n\n    // While `B` is VAR: extend it (as an optimization)\n    B = enter(net, B);\n\n    // Since `A` is VAR: point `A ~> B`.\n    if (true) {\n      // If B would leak...\n      if (is_global(A) && is_local(B)) {\n        // If B is a var, just swap it\n        if (is_var(B)) {\n          Port X = A; A = B; B = X;\n          continue;\n        }\n        // If B is a nod, create a leak interaction\n        if (is_nod(B)) {\n          //if (!TID()) printf(\"[%04x] NODE LEAK %s ~ %s\\n\", GID(), show_port(A).x, show_port(B).x);\n          push_redex(tm, new_pair(A, B));\n          break;\n        }\n      }\n\n      // Sanity check: if global A is unfilled, delay this link\n      if (is_global(A) && vars_load(net, get_val(A)) == 0) {\n        push_redex(tm, new_pair(A, B));\n        break;\n      }\n\n      // Stores `A -> B`, taking the current `A` subst as `A'`\n      Port A_ = vars_exchange(net, get_val(A), B);\n\n      // If there was no `A'`, stop, as we lost B's ownership\n      if (A_ == NONE) {\n        break;\n      }\n\n      #ifdef DEBUG\n      if (A_ == 0) printf(\"[%04x] ERR LINK %s ~ %s | %s ~ %s\\n\", GID(), show_port(INI_A).x, show_port(INI_B).x, show_port(A).x, show_port(B).x);\n      #endif\n\n      // Otherwise, delete `A` (we own both) and link `A' ~ B`\n      vars_take(net, get_val(A));\n      A = A_;\n    }\n  }\n}\n\n// Links `A ~ B` (as a pair).\n__device__ void link_pair(Net* net, TM* tm, Pair AB) {\n  link(net, tm, get_fst(AB), get_snd(AB));\n}\n\n// Resources\n// ---------\n\n// Gets the necessary resources for an interaction.\n__device__ bool get_resources(Net* net, TM* tm, u32 need_rbag, u32 need_node, u32 need_vars) {\n  u32 got_rbag = min(RLEN - tm->rbag.lo_end, RLEN - tm->rbag.hi_end);\n  u32 got_node;\n  u32 got_vars;\n  if (tm->mode != WORK) {\n    debug(\"allocating need_rbag=%u need_node=%u need_vars=%u\\n\", need_rbag, need_node, need_vars);\n\n    got_node = g_node_alloc(net, tm, need_node);\n    got_vars = g_vars_alloc(net, tm, need_vars);\n  } else {\n    got_node = l_node_alloc(net, tm, need_node);\n    got_vars = l_vars_alloc(net, tm, need_vars);\n  }\n  return got_rbag >= need_rbag && got_node >= need_node && got_vars >= need_vars;\n}\n\n// Interactions\n// ------------\n\n// The Link Interaction.\n__device__ bool interact_link(Net* net, TM* tm, Port a, Port b) {\n  // If A is a global var and B is a local node, leak it:\n  // ^A ~ (b1 b2)\n  // ------------- LEAK-NODE\n  // ^X ~ b1\n  // ^Y ~ b2\n  // ^A ~ ^(^X ^Y)\n  if (is_global(a) && is_nod(b) && is_local(b)) {\n    // Allocates needed nodes and vars.\n    if (!get_resources(net, tm, 3, 0, 0)) {\n      return false;\n    }\n\n    tm->leak += 1;\n\n    // Loads ports.\n    Pair l_b  = node_take(net, get_val(b));\n    Port l_b1 = enter(net, get_fst(l_b));\n    Port l_b2 = enter(net, get_snd(l_b));\n\n    // Leaks port 1.\n    Port g_b1;\n    if (is_local(l_b1)) {\n      g_b1 = new_port(VAR, g_vars_alloc_1(net));\n      vars_create(net, get_val(g_b1), NONE);\n      link_pair(net, tm, new_pair(g_b1, l_b1));\n    } else {\n      g_b1 = l_b1;\n    }\n\n    // Leaks port 2.\n    Port g_b2;\n    if (is_local(l_b2)) {\n      g_b2 = new_port(VAR, g_vars_alloc_1(net));\n      vars_create(net, get_val(g_b2), NONE);\n      link_pair(net, tm, new_pair(g_b2, l_b2));\n    } else {\n      g_b2 = l_b2;\n    }\n\n    // Leaks node.\n    Port g_b = new_port(get_tag(b), g_node_alloc_1(net));\n    node_create(net, get_val(g_b), new_pair(g_b1, g_b2));\n    link_pair(net, tm, new_pair(a, g_b));\n\n    return true;\n\n  // Otherwise, just perform a normal link.\n  } else {\n    // Allocates needed nodes and vars.\n    if (!get_resources(net, tm, 1, 0, 0)) {\n      return false;\n    }\n\n    link_pair(net, tm, new_pair(a, b));\n  }\n\n  return true;\n}\n\n// Declared here for use in call interactions.\nstatic inline bool interact_eras(Net* net, TM* tm, Port a, Port b);\n\n// The Call Interaction.\n#ifdef COMPILED\n///COMPILED_INTERACT_CALL///\n#else\n__device__ bool interact_eras(Net* net, TM* tm, Port a, Port b);\n__device__ bool interact_call(Net* net, TM* tm, Port a, Port b) {\n  // Loads Definition.\n  u32 fid  = get_val(a) & 0xFFFFFFF;\n  Def* def = &BOOK.defs_buf[fid];\n\n  // Copy Optimization.\n  if (def->safe && get_tag(b) == DUP) {\n    return interact_eras(net, tm, a, b);\n  }\n\n  // Allocates needed nodes and vars.\n  if (!get_resources(net, tm, def->rbag_len + 1, def->node_len, def->vars_len)) {\n    return false;\n  }\n\n  // Stores new vars.\n  for (u32 i = 0; i < def->vars_len; ++i) {\n    vars_create(net, tm->vloc[i], NONE);\n  }\n\n  // Stores new nodes.\n  for (u32 i = 0; i < def->node_len; ++i) {\n    node_create(net, tm->nloc[i], adjust_pair(net, tm, def->node_buf[i]));\n  }\n\n  // Links.\n  for (u32 i = 0; i < def->rbag_len; ++i) {\n    link_pair(net, tm, adjust_pair(net, tm, def->rbag_buf[i]));\n  }\n  link_pair(net, tm, new_pair(adjust_port(net, tm, def->root), b));\n\n  return true;\n}\n#endif\n\n// The Void Interaction.\n__device__ bool interact_void(Net* net, TM* tm, Port a, Port b) {\n  return true;\n}\n\n// The Eras Interaction.\n__device__ bool interact_eras(Net* net, TM* tm, Port a, Port b) {\n  // Allocates needed nodes and vars.\n  if (!get_resources(net, tm, 2, 0, 0)) {\n    return false;\n  }\n\n  // Loads ports.\n  Pair B  = node_take(net, get_val(b));\n  Port B1 = get_fst(B);\n  Port B2 = get_snd(B);\n\n  // Links.\n  link_pair(net, tm, new_pair(a, B1));\n  link_pair(net, tm, new_pair(a, B2));\n\n  return true;\n}\n\n// The Anni Interaction.\n__device__ bool interact_anni(Net* net, TM* tm, Port a, Port b) {\n  // Allocates needed nodes and vars.\n  if (!get_resources(net, tm, 2, 0, 0)) {\n    return false;\n  }\n\n  // Loads ports.\n  Pair A  = node_take(net, get_val(a));\n  Port A1 = get_fst(A);\n  Port A2 = get_snd(A);\n  Pair B  = node_take(net, get_val(b));\n  Port B1 = get_fst(B);\n  Port B2 = get_snd(B);\n\n  // Links.\n  link_pair(net, tm, new_pair(A1, B1));\n  link_pair(net, tm, new_pair(A2, B2));\n\n  return true;\n}\n\n// The Comm Interaction.\n__device__ bool interact_comm(Net* net, TM* tm, Port a, Port b) {\n  // Allocates needed nodes and vars.\n  if (!get_resources(net, tm, 4, 4, 4)) {\n    return false;\n  }\n\n  // Loads ports.\n  Pair A  = node_take(net, get_val(a));\n  Port A1 = get_fst(A);\n  Port A2 = get_snd(A);\n  Pair B  = node_take(net, get_val(b));\n  Port B1 = get_fst(B);\n  Port B2 = get_snd(B);\n\n  // Stores new vars.\n  vars_create(net, tm->vloc[0], NONE);\n  vars_create(net, tm->vloc[1], NONE);\n  vars_create(net, tm->vloc[2], NONE);\n  vars_create(net, tm->vloc[3], NONE);\n\n  // Stores new nodes.\n  node_create(net, tm->nloc[0], new_pair(new_port(VAR, tm->vloc[0]), new_port(VAR, tm->vloc[1])));\n  node_create(net, tm->nloc[1], new_pair(new_port(VAR, tm->vloc[2]), new_port(VAR, tm->vloc[3])));\n  node_create(net, tm->nloc[2], new_pair(new_port(VAR, tm->vloc[0]), new_port(VAR, tm->vloc[2])));\n  node_create(net, tm->nloc[3], new_pair(new_port(VAR, tm->vloc[1]), new_port(VAR, tm->vloc[3])));\n\n  // Links.\n  link_pair(net, tm, new_pair(new_port(get_tag(b), tm->nloc[0]), A1));\n  link_pair(net, tm, new_pair(new_port(get_tag(b), tm->nloc[1]), A2));\n  link_pair(net, tm, new_pair(new_port(get_tag(a), tm->nloc[2]), B1));\n  link_pair(net, tm, new_pair(new_port(get_tag(a), tm->nloc[3]), B2));\n\n  return true;\n}\n\n// The Oper Interaction.\n__device__ bool interact_oper(Net* net, TM* tm, Port a, Port b) {\n  // Allocates needed nodes and vars.\n  if (!get_resources(net, tm, 1, 1, 0)) {\n    return false;\n  }\n\n  // Loads ports.\n  Val  av = get_val(a);\n  Pair B  = node_take(net, get_val(b));\n  Port B1 = get_fst(B);\n  Port B2 = enter(net, get_snd(B));\n\n  // Performs operation.\n  if (get_tag(B1) == NUM) {\n    Val  bv = get_val(B1);\n    Numb cv = operate(av, bv);\n    link_pair(net, tm, new_pair(new_port(NUM, cv), B2));\n  } else {\n    node_create(net, tm->nloc[0], new_pair(a, B2));\n    link_pair(net, tm, new_pair(B1, new_port(OPR, tm->nloc[0])));\n  }\n\n  return true;\n}\n\n// The Swit Interaction.\n__device__ bool interact_swit(Net* net, TM* tm, Port a, Port b) {\n  // Allocates needed nodes and vars.\n  if (!get_resources(net, tm, 1, 2, 0)) {\n    return false;\n  }\n\n  // Loads ports.\n  u32  av = get_u24(get_val(a));\n  Pair B  = node_take(net, get_val(b));\n  Port B1 = get_fst(B);\n  Port B2 = get_snd(B);\n\n  // Stores new nodes.\n  if (av == 0) {\n    node_create(net, tm->nloc[0], new_pair(B2, new_port(ERA,0)));\n    link_pair(net, tm, new_pair(new_port(CON, tm->nloc[0]), B1));\n  } else {\n    node_create(net, tm->nloc[0], new_pair(new_port(ERA,0), new_port(CON, tm->nloc[1])));\n    node_create(net, tm->nloc[1], new_pair(new_port(NUM, new_u24(av-1)), B2));\n    link_pair(net, tm, new_pair(new_port(CON, tm->nloc[0]), B1));\n  }\n\n  return true;\n}\n\n// Pops a local redex and performs a single interaction.\n__device__ bool interact(Net* net, TM* tm, Pair redex, u32 turn) {\n  // Gets redex ports A and B.\n  Port a = get_fst(redex);\n  Port b = get_snd(redex);\n\n  // Gets the rule type.\n  Rule rule = get_rule(a, b);\n\n  // If there is no redex, stop.\n  if (redex != 0) {\n    //if (GID() == 0 && turn == 0x201) {\n      //Pair kn = get_tag(b) == CON ? node_load(net, get_val(b)) : 0;\n      //printf(\"%04x:[%04x] REDUCE %s ~ %s | par? %d | (%s %s)\\n\",\n        //turn, GID(),\n        //show_port(get_fst(redex)).x,\n        //show_port(get_snd(redex)).x,\n        //get_par_flag(redex),\n        //show_port(get_fst(kn)).x,\n        //show_port(get_snd(kn)).x);\n    //}\n\n    // Used for root redex.\n    if (get_tag(a) == REF && b == ROOT) {\n      rule = CALL;\n    // Swaps ports if necessary.\n    } else if (should_swap(a,b)) {\n      swap(&a, &b);\n    }\n\n    // Dispatches interaction rule.\n    bool success;\n    switch (rule) {\n      case LINK: success = interact_link(net, tm, a, b); break;\n      case CALL: success = interact_call(net, tm, a, b); break;\n      case VOID: success = interact_void(net, tm, a, b); break;\n      case ERAS: success = interact_eras(net, tm, a, b); break;\n      case ANNI: success = interact_anni(net, tm, a, b); break;\n      case COMM: success = interact_comm(net, tm, a, b); break;\n      case OPER: success = interact_oper(net, tm, a, b); break;\n      case SWIT: success = interact_swit(net, tm, a, b); break;\n    }\n\n    // If error, pushes redex back.\n    if (!success) {\n      push_redex(tm, redex);\n      return false;\n    // Else, increments the interaction count.\n    } else if (rule != LINK) {\n      tm->itrs += 1;\n    }\n  }\n\n  return true;\n}\n\n// RBag Save/Load\n// --------------\n\n// Moves redexes from shared memory to global bag\n__device__ void save_redexes(Net* net, TM *tm, u32 turn) {\n  u32 idx = 0;\n  u32 bag = tm->mode == SEED ? transpose(GID(), TPB, BPG) : GID();\n\n  // Leaks low-priority redexes\n  for (u32 i = 0; i < tm->rbag.lo_end; ++i) {\n    Pair R = tm->rbag.lo_buf[i % RLEN];\n    Port x = get_fst(R);\n    Port y = get_snd(R);\n    Port X = new_port(VAR, g_vars_alloc_1(net));\n    Port Y = new_port(VAR, g_vars_alloc_1(net));\n    vars_create(net, get_val(X), NONE);\n    vars_create(net, get_val(Y), NONE);\n    link_pair(net, tm, new_pair(X, x));\n    link_pair(net, tm, new_pair(Y, y));\n    net->g_rbag_buf_B[bag * RLEN + (idx++)] = new_pair(X, Y);\n  }\n  __syncthreads();\n  tm->rbag.lo_end = 0;\n\n  // Executes all high-priority redexes\n  while (rbag_has_highs(&tm->rbag)) {\n    Pair redex = pop_redex(tm);\n    if (!interact(net, tm, redex, turn)) {\n      printf(\"ERROR: failed to clear high-priority redexes\");\n    }\n  }\n  __syncthreads();\n\n  #ifdef DEBUG\n  if (rbag_len(&tm->rbag) > 0) printf(\"[%04x] ERR SAVE_REDEXES lo=%d hi=%d tot=%d\\n\", GID(), tm->rbag.lo_end, tm->rbag.hi_end, rbag_len(&tm->rbag));\n  #endif\n\n  // Updates global redex counter\n  atomicAdd(net->g_rbag_use_B, idx);\n}\n\n// Loads redexes from global bag to shared memory\n// FIXME: check if we have enuogh space for all loads\n__device__ void load_redexes(Net* net, TM *tm, u32 turn) {\n  u32 gid = BID() * TPB + TID();\n  u32 bag = tm->mode == SEED ? transpose(GID(), TPB, BPG) : GID();\n  for (u32 i = 0; i < RLEN; ++i) {\n    Pair redex = atomicExch(&net->g_rbag_buf_A[bag * RLEN + i], 0);\n    if (redex != 0) {\n      Port a = enter(net, get_fst(redex));\n      Port b = enter(net, get_snd(redex));\n      #ifdef DEBUG\n      if (is_local(a) || is_local(b)) printf(\"[%04x] ERR LOAD_REDEXES\\n\", turn);\n      #endif\n      push_redex(tm, new_pair(a, b));\n    } else {\n      break;\n    }\n  }\n  __syncthreads();\n}\n\n// Kernels\n// -------\n\n// Sets the initial redex.\n__global__ void boot_redex(GNet* gnet, Pair redex) {\n  // Creates root variable.\n  gnet->vars_buf[get_val(ROOT)] = NONE;\n  // Creates root redex.\n  if (gnet->turn % 2 == 0) {\n    gnet->rbag_buf_A[0] = redex;\n  } else {\n    gnet->rbag_buf_B[0] = redex;\n  }\n}\n\n// Creates a node.\n__global__ void make_node(GNet* gnet, Tag tag, Port fst, Port snd, Port* ret) {\n  if (GID() == 0) {\n    Net net = vnet_new(gnet, NULL, gnet->turn);\n    u32 loc = g_node_alloc_1(&net);\n    node_create(&net, loc, new_pair(fst, snd));\n    *ret = new_port(tag, loc);\n  }\n}\n\n__global__ void inbetween(GNet* gnet) {\n  // Clears rbag use counter\n  if (gnet->turn % 2 == 0) {\n    gnet->rbag_use_A = 0;\n  } else {\n    gnet->rbag_use_B = 0;\n  }\n\n  // Increments gnet turn\n  gnet->turn += 1;\n\n  // Increments interaction counter\n  gnet->itrs += gnet->iadd;\n\n  // Resets the rdec variable\n  gnet->rdec = 0;\n\n  // Moves to next mode\n  if (!gnet->down) {\n    gnet->mode = min(gnet->mode + 1, WORK);\n  }\n\n  // If no work was done...\n  if (gnet->iadd == 0) {\n    // If on seed mode, go up to GROW mode\n    if (gnet->mode == SEED) {\n      gnet->mode = GROW;\n      gnet->down = 0;\n    // Otherwise, go down to SEED mode\n    } else {\n      gnet->mode = SEED;\n      gnet->down = 1;\n      gnet->rdec = 1; // peel one rpos\n    }\n    //printf(\">> CHANGE MODE TO %d | %d <<\\n\", gnet->mode, gnet->down);\n  }\n\n  // Reset interaction adder\n  gnet->iadd = 0;\n}\n\n// EVAL\n__global__ void evaluator(GNet* gnet) {\n  extern __shared__ char shared_mem[]; // 96 KB\n  __shared__ Pair spawn[TPB]; // thread initialized\n\n  // Thread Memory\n  TM tm = tmem_new();\n\n  // Net (Local-Global View)\n  Net net = vnet_new(gnet, shared_mem, gnet->turn);\n\n  // Clears shared memory\n  for (u32 i = 0; i < L_NODE_LEN / TPB; ++i) {\n    net.l_node_buf[i * TPB + TID()] = 0;\n    net.l_vars_buf[i * TPB + TID()] = 0;\n  }\n  __syncthreads();\n\n  // Sets mode\n  tm.mode = gnet->mode;\n\n  // Loads Redexes\n  load_redexes(&net, &tm, gnet->turn);\n\n  // Clears spawn buffer\n  spawn[TID()] = rbag_len(&tm.rbag) > 0 ? 0xFFFFFFFFFFFFFFFF : 0;\n  __syncthreads();\n\n  // Variables\n  u64 INIT = clock64(); // initial time\n  u32 HASR = block_count(rbag_len(&tm.rbag) > 0);\n  u32 tick = 0;\n  u32 bag  = tm.mode == SEED ? transpose(GID(), TPB, BPG) : GID();\n  u32 rpos = gnet->rbag_pos[bag] > 0 ? gnet->rbag_pos[bag] - gnet->rdec : gnet->rbag_pos[bag];\n  u8  down = gnet->down;\n\n  //if (BID() == 0 && gnet->turn == 0x69) {\n    //printf(\"[%04x] ini rpos is %d | bag=%d\\n\", GID(), rpos, bag);\n  //}\n\n  // Aborts if empty\n  if (HASR == 0) {\n    return;\n  }\n\n  //if (BID() == 0 && rbag_len(&tm.rbag) > 0) {\n    //Pair redex = pop_redex(&tm);\n    //Pair kn = get_tag(get_snd(redex)) == CON ? node_load(&net, get_val(get_snd(redex))) : 0;\n    //printf(\"[%04x] HAS REDEX %s ~ %s | par? %d | (%s %s)\\n\",\n      //GID(),\n      //show_port(get_fst(redex)).x,\n      //show_port(get_snd(redex)).x,\n      //get_par_flag(redex),\n      //show_port(get_fst(kn)).x,\n      //show_port(get_snd(kn)).x);\n    //push_redex(&tm, redex);\n  //}\n\n  //// Display debug rbag\n  //if (GID() == 0) {\n    //print_rbag(&net, &tm);\n    //printf(\"| rbag_pos = %d | mode = %d | down = %d | turn = %04x\\n\", gnet->rbag_pos[bag], gnet->mode, down, gnet->turn);\n  //}\n  //__syncthreads();\n\n  // GROW MODE\n  // ---------\n\n  if (tm.mode == SEED || tm.mode == GROW) {\n    u32 tlim = tm.mode == SEED ? min(TPB_L2,BPG_L2) : max(TPB_L2,BPG_L2);\n    u32 span = 1 << (32 - __clz(TID()));\n\n    Pair redex;\n\n    for (u32 tick = 0; tick < tlim; ++tick) {\n      u32 span = 1 << tick;\n      u32 targ = TID() ^ span;\n\n      // Attempts to spawn a thread\n      if (TID() < span && spawn[targ] == 0) {\n        //if (BID() == 0) {\n          //if (!TID()) printf(\"----------------------------------------------------\\n\");\n          //if (!TID()) printf(\"TIC %04x | span=%d | rlen=%d | \", tick, span, rbag_len(&tm.rbag));\n          //block_print(rbag_len(&tm.rbag));\n          //if (!TID()) printf(\"\\n\");\n          //__syncthreads();\n        //}\n\n        // Performs some interactions until a parallel redex is found\n        for (u32 i = 0; i < 64; ++i) {\n          if (tm.rbag.lo_end < rpos) break;\n          redex = pop_redex(&tm);\n          if (redex == 0) {\n            break;\n          }\n          // If we found a stealable redex, pass it to stealing,\n          // and un-mark the redex above it, so we keep it for us.\n          if (get_par_flag(redex)) {\n            Pair above = pop_redex(&tm);\n            if (above != 0) {\n              push_redex(&tm, clr_par_flag(above));\n            }\n            break;\n          }\n          interact(&net, &tm, redex, gnet->turn);\n          redex = 0;\n          while (tm.rbag.hi_end > 0) {\n            if (!interact(&net, &tm, pop_redex(&tm), gnet->turn)) break;\n          }\n        }\n\n        // Spawn a thread\n        if (redex != 0 && get_par_flag(redex)) {\n          //if (BID() == 0) {\n            //Pair kn = get_tag(get_snd(redex)) == CON ? node_load(&net, get_val(get_snd(redex))) : 0;\n            //printf(\"[%04x] GIVE %s ~ %s | par? %d | (%s %s) | rbag.lo_end=%d\\n\", GID(), show_port(get_fst(redex)).x, show_port(get_snd(redex)).x, get_par_flag(redex), show_port(peek(&net, &tm, get_fst(kn))).x, show_port(peek(&net, &tm, get_snd(kn))).x, tm.rbag.lo_end);\n          //}\n\n          spawn[targ] = clr_par_flag(redex);\n          if (!down) {\n            rpos = tm.rbag.lo_end - 1;\n          }\n        }\n      }\n      __syncthreads();\n\n      // If we've been spawned, push initial redex\n      if (TID() >= span && TID() < span*2 && spawn[TID()] != 0 && spawn[TID()] != 0xFFFFFFFFFFFFFFFF) {\n        //if (rbag_len(&tm.rbag) > 0) {\n          //printf(\"[%04x] ERROR: SPAWNED BUT HAVE REDEX\\n\", GID());\n        //}\n\n        push_redex(&tm, atomicExch(&spawn[TID()], 0xFFFFFFFFFFFFFFFF));\n        rpos = 0;\n        //if (BID() == 0) printf(\"[%04x] TAKE %016llx\\n\", GID(), spawn[TID()]);\n      }\n      __syncthreads();\n\n      //if (BID() == 0) {\n        //if (!TID()) printf(\"TAC %04x | span=%d | rlen=%d | \", tick, span, rbag_len(&tm.rbag));\n        //block_print(rbag_len(&tm.rbag));\n        //if (!TID()) printf(\"\\n\");\n        //__syncthreads();\n      //}\n      //__syncthreads();\n\n      //printf(\"[%04x] span is %d\\n\", TID(), span);\n      //__syncthreads();\n    }\n\n    //if (BID() == 0 && gnet->turn == 0x69) {\n      //printf(\"[%04x] end rpos is %d | bag=%d\\n\", GID(), rpos, bag);\n    //}\n\n    gnet->rbag_pos[bag] = rpos;\n\n  }\n\n  // WORK MODE\n  // ---------\n\n  if (tm.mode == WORK) {\n    u32 chkt = 0;\n    u32 chka = 1;\n    u32 bag  = tm.mode == SEED ? transpose(GID(), TPB, BPG) : GID();\n    u32 rpos = gnet->rbag_pos[bag];\n    for (tick = 0; tick < 1 << 9; ++tick) {\n      if (tm.rbag.lo_end > rpos || rbag_has_highs(&tm.rbag)) {\n        if (interact(&net, &tm, pop_redex(&tm), gnet->turn)) {\n          while (rbag_has_highs(&tm.rbag)) {\n            if (!interact(&net, &tm, pop_redex(&tm), gnet->turn)) break;\n          }\n        }\n      }\n      __syncthreads();\n    }\n  }\n  __syncthreads();\n\n  //u32 ITRS = block_sum(tm.itrs);\n  //u32 LOOP = block_sum((u32)tick);\n  //u32 RLEN = block_sum(rbag_len(&tm.rbag));\n  //u32 FAIL = 0; // block_sum((u32)fail);\n  //f64 TIME = (f64)(clock64() - INIT) / (f64)S;\n  //f64 MIPS = (f64)ITRS / TIME / (f64)1000000.0;\n  ////if (BID() >= 0 && TID() == 0) {\n  //if (TID() == 0) {\n    //printf(\"%04x:[%02x]: MODE=%d DOWN=%d ITRS=%d LOOP=%d RLEN=%d FAIL=%d TIME=%f MIPS=%.0f | %d\\n\",\n      //gnet->turn, BID(), tm.mode, down, ITRS, LOOP, RLEN, FAIL, TIME, MIPS, 42);\n  //}\n  //__syncthreads();\n\n  // Display debug rbag\n  //if (BID() == 0) {\n    //for (u32 i = 0; i < TPB; ++i) {\n      //if (TID() == i && rbag_len(&tm.rbag) > 0) print_rbag(&net, &tm);\n      //__syncthreads();\n    //}\n    //__syncthreads();\n  //}\n\n  // Moves rbag to global\n  save_redexes(&net, &tm, gnet->turn);\n\n  // Stores rewrites\n  atomicAdd(&gnet->iadd, tm.itrs);\n  atomicAdd(&gnet->leak, tm.leak);\n\n}\n\n// GNet Host Functions\n// -------------------\n\n// Initializes the GNet\n__global__ void initialize(GNet* gnet) {\n  gnet->node_put[GID()] = 0;\n  gnet->vars_put[GID()] = 0;\n  gnet->rbag_pos[GID()] = 0;\n  for (u32 i = 0; i < RLEN; ++i) {\n    gnet->rbag_buf_A[G_RBAG_LEN / TPG * GID() + i] = 0;\n  }\n  for (u32 i = 0; i < RLEN; ++i) {\n    gnet->rbag_buf_B[G_RBAG_LEN / TPG * GID() + i] = 0;\n  }\n}\n\nGNet* gnet_create() {\n  GNet *gnet;\n  cudaMalloc((void**)&gnet, sizeof(GNet));\n  initialize<<<BPG, TPB>>>(gnet);\n  //cudaMemset(gnet, 0, sizeof(GNet));\n  return gnet;\n}\n\nu32 gnet_get_rlen(GNet* gnet, u32 turn) {\n  u32 rbag_use;\n  if (turn % 2 == 0) {\n    cudaMemcpy(&rbag_use, &gnet->rbag_use_B, sizeof(u32), cudaMemcpyDeviceToHost);\n  } else {\n    cudaMemcpy(&rbag_use, &gnet->rbag_use_A, sizeof(u32), cudaMemcpyDeviceToHost);\n  }\n  return rbag_use;\n}\n\nu64 gnet_get_itrs(GNet* gnet) {\n  u64 itrs;\n  cudaMemcpy(&itrs, &gnet->itrs, sizeof(u64), cudaMemcpyDeviceToHost);\n  return itrs;\n}\n\nu64 gnet_get_leak(GNet* gnet) {\n  u64 leak;\n  cudaMemcpy(&leak, &gnet->leak, sizeof(u64), cudaMemcpyDeviceToHost);\n  return leak;\n}\n\nvoid gnet_boot_redex(GNet* gnet, Pair redex) {\n  boot_redex<<<BPG, TPB>>>(gnet, redex);\n}\n\nvoid gnet_normalize(GNet* gnet) {\n  // Invokes the Evaluator Kernel repeatedly\n  u32 turn;\n  u64 itrs = 0;\n  u32 rlen = 0;\n  // NORM\n  for (turn = 0; turn < 0xFFFFFFFF; ++turn) {\n    //printf(\"\\e[1;1H\\e[2J\");\n    //printf(\"==================================================== \");\n    //printf(\"TURN: %04x | RLEN: %04x | ITRS: %012llu\\n\", turn, rlen, itrs);\n    //cudaDeviceSynchronize();\n\n    evaluator<<<BPG, TPB, sizeof(LNet)>>>(gnet);\n    inbetween<<<1, 1>>>(gnet);\n    //cudaDeviceSynchronize();\n\n    //count_memory<<<BPG, TPB>>>(gnet);\n    //cudaDeviceSynchronize();\n\n    //print_heatmap<<<1,1>>>(gnet, turn+1);\n    //cudaDeviceSynchronize();\n\n    itrs = gnet_get_itrs(gnet);\n    rlen = gnet_get_rlen(gnet, turn);\n    if (rlen == 0) {\n      //printf(\"Completed after %d kernel launches!\\n\", turn);\n      break;\n    }\n  }\n}\n\n// Reads a device node to host\nPair gnet_node_load(GNet* gnet, u32 loc) {\n  Pair pair;\n  cudaMemcpy(&pair, &gnet->node_buf[loc], sizeof(Pair), cudaMemcpyDeviceToHost);\n  return pair;\n}\n\n// Reads a device var to host\nPort gnet_vars_load(GNet* gnet, u32 loc) {\n  Pair port;\n  cudaMemcpy(&port, &gnet->vars_buf[loc], sizeof(Port), cudaMemcpyDeviceToHost);\n  return port;\n}\n\n// Writes a host var to device\nvoid gnet_vars_create(GNet* gnet, u32 var, Port val) {\n  cudaMemcpy(&gnet->vars_buf[var], &val, sizeof(Port), cudaMemcpyHostToDevice);\n}\n\n// Like the enter() function, but from host and read-only\nPort gnet_peek(GNet* gnet, Port port) {\n  while (get_tag(port) == VAR) {\n    Port val = gnet_vars_load(gnet, get_val(port));\n    if (val == NONE) break;\n    port = val;\n  }\n  return port;\n}\n\n// Expands a REF Port.\nPort gnet_expand(GNet* gnet, Port port) {\n  Port old = gnet_vars_load(gnet, get_val(ROOT));\n  Port got = gnet_peek(gnet, port);\n  //printf(\"expand %s\\n\", show_port(got).x);\n  while (get_tag(got) == REF) {\n    gnet_boot_redex(gnet, new_pair(got, ROOT));\n    gnet_normalize(gnet);\n    got = gnet_peek(gnet, gnet_vars_load(gnet, get_val(ROOT)));\n  }\n  gnet_vars_create(gnet, get_val(ROOT), old);\n  return got;\n}\n\n// Allocs and creates a node, returning its port.\nPort gnet_make_node(GNet* gnet, Tag tag, Port fst, Port snd) {\n  Port ret;\n  Port* d_ret;\n  cudaMalloc(&d_ret, sizeof(Port));\n  make_node<<<1,1>>>(gnet, tag, fst, snd, d_ret);\n  cudaMemcpy(&ret, d_ret, sizeof(Port), cudaMemcpyDeviceToHost);\n  cudaFree(d_ret);\n  return ret;\n}\n\n// Book Loader\n// -----------\n\nbool book_load(Book* book, u32* buf) {\n  // Reads defs_len\n  book->defs_len = *buf++;\n\n  // Parses each def\n  for (u32 i = 0; i < book->defs_len; ++i) {\n    // Reads fid\n    u32 fid = *buf++;\n\n    // Gets def\n    Def* def = &book->defs_buf[fid];\n\n    // Reads name\n    memcpy(def->name, buf, 256);\n    buf += 64;\n\n    // Reads safe flag\n    def->safe = *buf++;\n\n    // Reads lengths\n    def->rbag_len = *buf++;\n    def->node_len = *buf++;\n    def->vars_len = *buf++;\n\n    if (def->rbag_len > L_NODE_LEN/TPB) {\n      fprintf(stderr, \"def '%s' has too many redexes: %u\\n\", def->name, def->rbag_len);\n      return false;\n    }\n\n    if (def->node_len > L_NODE_LEN/TPB) {\n      fprintf(stderr, \"def '%s' has too many nodes: %u\\n\", def->name, def->node_len);\n      return false;\n    }\n\n    // Reads root\n    def->root = *buf++;\n\n    // Reads rbag_buf\n    memcpy(def->rbag_buf, buf, 8*def->rbag_len);\n    buf += def->rbag_len * 2;\n\n    // Reads node_buf\n    memcpy(def->node_buf, buf, 8*def->node_len);\n    buf += def->node_len * 2;\n  }\n\n  return true;\n}\n\n// Debug Printing\n// --------------\n\n__device__ __host__ void put_u32(char* B, u32 val) {\n  for (int i = 0; i < 8; i++, val >>= 4) {\n    B[8-i-1] = \"0123456789ABCDEF\"[val & 0xF];\n  }\n}\n\n__device__ __host__ Show show_port(Port port) {\n  // NOTE: this is done like that because sprintf seems not to be working\n  Show s;\n  switch (get_tag(port)) {\n    case VAR: memcpy(s.x, \"VAR:\", 4); put_u32(s.x+4, get_val(port)); break;\n    case REF: memcpy(s.x, \"REF:\", 4); put_u32(s.x+4, get_val(port)); break;\n    case ERA: memcpy(s.x, \"ERA:________\", 12); break;\n    case NUM: memcpy(s.x, \"NUM:\", 4); put_u32(s.x+4, get_val(port)); break;\n    case CON: memcpy(s.x, \"CON:\", 4); put_u32(s.x+4, get_val(port)); break;\n    case DUP: memcpy(s.x, \"DUP:\", 4); put_u32(s.x+4, get_val(port)); break;\n    case OPR: memcpy(s.x, \"OPR:\", 4); put_u32(s.x+4, get_val(port)); break;\n    case SWI: memcpy(s.x, \"SWI:\", 4); put_u32(s.x+4, get_val(port)); break;\n  }\n  s.x[12] = '\\0';\n  return s;\n}\n\n__device__ Show show_rule(Rule rule) {\n  Show s;\n  switch (rule) {\n    case LINK: memcpy(s.x, \"LINK\", 4); break;\n    case VOID: memcpy(s.x, \"VOID\", 4); break;\n    case ERAS: memcpy(s.x, \"ERAS\", 4); break;\n    case ANNI: memcpy(s.x, \"ANNI\", 4); break;\n    case COMM: memcpy(s.x, \"COMM\", 4); break;\n    case OPER: memcpy(s.x, \"OPER\", 4); break;\n    case SWIT: memcpy(s.x, \"SWIT\", 4); break;\n    case CALL: memcpy(s.x, \"CALL\", 4); break;\n    default  : memcpy(s.x, \"????\", 4); break;\n  }\n  s.x[4] = '\\0';\n  return s;\n}\n\n__device__ void print_rbag(Net* net, TM* tm) {\n  printf(\"RBAG | FST-TREE     | SND-TREE    \\n\");\n  printf(\"---- | ------------ | ------------\\n\");\n  for (u32 i = 0; i < tm->rbag.hi_end; ++i) {\n    Pair redex = tm->rbag.hi_buf[i];\n    Pair node1 = get_tag(get_snd(redex)) == CON ? node_load(net, get_val(get_fst(redex))) : 0;\n    Pair node2 = get_tag(get_snd(redex)) == CON ? node_load(net, get_val(get_snd(redex))) : 0;\n    printf(\"%04X | %s | %s | hi | (%s %s) ~ (%s %s)\\n\", i,\n      show_port(get_fst(redex)).x,\n      show_port(get_snd(redex)).x,\n      show_port(peek(net, get_fst(node1))).x,\n      show_port(peek(net, get_snd(node1))).x,\n      show_port(peek(net, get_fst(node2))).x,\n      show_port(peek(net, get_snd(node2))).x);\n  }\n  for (u32 i = 0; i < tm->rbag.lo_end; ++i) {\n    Pair redex = tm->rbag.lo_buf[i%RLEN];\n    Pair node1 = get_tag(get_snd(redex)) == CON ? node_load(net, get_val(get_fst(redex))) : 0;\n    Pair node2 = get_tag(get_snd(redex)) == CON ? node_load(net, get_val(get_snd(redex))) : 0;\n    printf(\"%04X | %s | %s | hi | (%s %s) ~ (%s %s)\\n\", i,\n      show_port(get_fst(redex)).x,\n      show_port(get_snd(redex)).x,\n      show_port(peek(net, get_fst(node1))).x,\n      show_port(peek(net, get_snd(node1))).x,\n      show_port(peek(net, get_fst(node2))).x,\n      show_port(peek(net, get_snd(node2))).x);\n  }\n  printf(\"==== | ============ | ============\\n\");\n}\n\n__device__ __host__ void print_net(Net* net, u32 ini, u32 end) {\n  printf(\"NODE | PORT-1       | PORT-2      \\n\");\n  printf(\"---- | ------------ | ------------\\n\");\n  for (u32 i = ini; i < end; ++i) {\n    Pair node = node_load(net, i);\n    if (node != 0) {\n      printf(\"%04X | %s | %s\\n\", i, show_port(get_fst(node)).x, show_port(get_snd(node)).x);\n    }\n  }\n  printf(\"==== | ============ |\\n\");\n  printf(\"VARS | VALUE        |\\n\");\n  printf(\"---- | ------------ |\\n\");\n  for (u32 i = ini; i < end; ++i) {\n    Port var = vars_load(net,i);\n    if (var != 0) {\n      printf(\"%04X | %s |\\n\", i, show_port(vars_load(net,i)).x);\n    }\n  }\n  printf(\"==== | ============ |\\n\");\n}\n\n__device__ void pretty_print_numb(Numb word) {\n  switch (get_typ(word)) {\n    case TY_SYM: {\n      switch (get_sym(word)) {\n        // types\n        case TY_U24: printf(\"[u24]\"); break;\n        case TY_I24: printf(\"[i24]\"); break;\n        case TY_F24: printf(\"[f24]\"); break;\n        // operations\n        case OP_ADD: printf(\"[+]\"); break;\n        case OP_SUB: printf(\"[-]\"); break;\n        case FP_SUB: printf(\"[:-]\"); break;\n        case OP_MUL: printf(\"[*]\"); break;\n        case OP_DIV: printf(\"[/]\"); break;\n        case FP_DIV: printf(\"[:/]\"); break;\n        case OP_REM: printf(\"[%%]\"); break;\n        case FP_REM: printf(\"[:%%]\"); break;\n        case OP_EQ:  printf(\"[=]\"); break;\n        case OP_NEQ: printf(\"[!]\"); break;\n        case OP_LT:  printf(\"[<]\"); break;\n        case OP_GT:  printf(\"[>]\"); break;\n        case OP_AND: printf(\"[&]\"); break;\n        case OP_OR:  printf(\"[|]\"); break;\n        case OP_XOR: printf(\"[^]\"); break;\n        case OP_SHL: printf(\"[<<]\"); break;\n        case FP_SHL: printf(\"[:<<]\"); break;\n        case OP_SHR: printf(\"[>>]\"); break;\n        case FP_SHR: printf(\"[:>>]\"); break;\n        default:     printf(\"[?]\"); break;\n      }\n      break;\n    }\n    case TY_U24: {\n      printf(\"%u\", get_u24(word));\n      break;\n    }\n    case TY_I24: {\n      printf(\"%+d\", get_i24(word));\n      break;\n    }\n    case TY_F24: {\n      if (isinf(get_f24(word))) {\n        if (signbit(get_f24(word))) {\n          printf(\"-inf\");\n        } else {\n          printf(\"+inf\");\n        }\n      } else if (isnan(get_f24(word))) {\n        printf(\"+NaN\");\n      } else {\n        printf(\"%.7e\", get_f24(word));\n      }\n      break;\n    }\n    default: {\n      switch (get_typ(word)) {\n        case OP_ADD: printf(\"[+0x%07X]\", get_u24(word)); break;\n        case OP_SUB: printf(\"[-0x%07X]\", get_u24(word)); break;\n        case FP_SUB: printf(\"[:-0x%07X]\", get_u24(word)); break;\n        case OP_MUL: printf(\"[*0x%07X]\", get_u24(word)); break;\n        case OP_DIV: printf(\"[/0x%07X]\", get_u24(word)); break;\n        case FP_DIV: printf(\"[:/0x%07X]\", get_u24(word)); break;\n        case OP_REM: printf(\"[%%0x%07X]\", get_u24(word)); break;\n        case FP_REM: printf(\"[:%%0x%07X]\", get_u24(word)); break;\n        case OP_EQ:  printf(\"[=0x%07X]\", get_u24(word)); break;\n        case OP_NEQ: printf(\"[!0x%07X]\", get_u24(word)); break;\n        case OP_LT:  printf(\"[<0x%07X]\", get_u24(word)); break;\n        case OP_GT:  printf(\"[>0x%07X]\", get_u24(word)); break;\n        case OP_AND: printf(\"[&0x%07X]\", get_u24(word)); break;\n        case OP_OR:  printf(\"[|0x%07X]\", get_u24(word)); break;\n        case OP_XOR: printf(\"[^0x%07X]\", get_u24(word)); break;\n        case OP_SHL: printf(\"[<<0x%07X]\", get_u24(word)); break;\n        case FP_SHL: printf(\"[:<<0x%07X]\", get_u24(word)); break;\n        case OP_SHR: printf(\"[>>0x%07X]\", get_u24(word)); break;\n        case FP_SHR: printf(\"[:>>0x%07X]\", get_u24(word)); break;\n        default:     printf(\"[?0x%07X]\", get_u24(word)); break;\n      }\n      break;\n    }\n  }\n}\n\n__device__ void pretty_print_port(Net* net, Port port) {\n  Port stack[4096];\n  stack[0] = port;\n  u32 len = 1;\n  while (len > 0) {\n    if (len > 256) {\n      printf(\"ERROR: result too deep to print. This will be fixed soon(TM)\");\n      --len;\n      continue;\n    }\n    Port cur = stack[--len];\n    switch (get_tag(cur)) {\n      case CON: {\n        Pair node = node_load(net,get_val(cur));\n        Port p2   = get_snd(node);\n        Port p1   = get_fst(node);\n        printf(\"(\");\n        stack[len++] = new_port(ERA, (u32)(')'));\n        stack[len++] = p2;\n        stack[len++] = new_port(ERA, (u32)(' '));\n        stack[len++] = p1;\n        break;\n      }\n      case ERA: {\n        if (get_val(cur) != 0) {\n          printf(\"%c\", (char)get_val(cur));\n        } else {\n          printf(\"*\");\n        }\n        break;\n      }\n      case VAR: {\n        Port got = vars_load(net, get_val(cur));\n        if (got != NONE) {\n          stack[len++] = got;\n        } else {\n          printf(\"x%x\", get_val(cur));\n        }\n        break;\n      }\n      case NUM: {\n        pretty_print_numb(get_val(cur));\n        break;\n      }\n      case DUP: {\n        Pair node = node_load(net,get_val(cur));\n        Port p2   = get_snd(node);\n        Port p1   = get_fst(node);\n        printf(\"{\");\n        stack[len++] = new_port(ERA, (u32)('}'));\n        stack[len++] = p2;\n        stack[len++] = new_port(ERA, (u32)(' '));\n        stack[len++] = p1;\n        break;\n      }\n      case OPR: {\n        Pair node = node_load(net,get_val(cur));\n        Port p2   = get_snd(node);\n        Port p1   = get_fst(node);\n        printf(\"$(\");\n        stack[len++] = new_port(ERA, (u32)(')'));\n        stack[len++] = p2;\n        stack[len++] = new_port(ERA, (u32)(' '));\n        stack[len++] = p1;\n        break;\n      }\n      case SWI: {\n        Pair node = node_load(net,get_val(cur));\n        Port p2   = get_snd(node);\n        Port p1   = get_fst(node);\n        printf(\"?(\");\n        stack[len++] = new_port(ERA, (u32)(')'));\n        stack[len++] = p2;\n        stack[len++] = new_port(ERA, (u32)(' '));\n        stack[len++] = p1;\n        break;\n      }\n      case REF: {\n        u32  fid = get_val(cur) & 0xFFFFFFF;\n        Def* def = &BOOK.defs_buf[fid];\n        printf(\"@%s\", def->name);\n        break;\n      }\n    }\n  }\n}\n\n__device__ void pretty_print_rbag(Net* net, RBag* rbag) {\n  for (u32 i = 0; i < rbag->lo_end; ++i) {\n    Pair redex = rbag->lo_buf[i%RLEN];\n    if (redex != 0) {\n      pretty_print_port(net, get_fst(redex));\n      printf(\" ~ \");\n      pretty_print_port(net, get_snd(redex));\n      printf(\"\\n\");\n    }\n  }\n  for (u32 i = 0; i < rbag->hi_end; ++i) {\n    Pair redex = rbag->hi_buf[i];\n    if (redex != 0) {\n      pretty_print_port(net, get_fst(redex));\n      printf(\" ~ \");\n      pretty_print_port(net, get_snd(redex));\n      printf(\"\\n\");\n    }\n  }\n}\n\n__device__ u32 NODE_COUNT;\n__device__ u32 VARS_COUNT;\n\n__global__ void count_memory(GNet* gnet) {\n  u32 node_count = 0;\n  u32 vars_count = 0;\n  for (u32 i = GID(); i < G_NODE_LEN; i += TPG) {\n    if (gnet->node_buf[i] != 0) ++node_count;\n    if (gnet->vars_buf[i] != 0) ++vars_count;\n  }\n\n  __shared__ u32 block_node_count;\n  __shared__ u32 block_vars_count;\n\n  if (TID() == 0) block_node_count = 0;\n  if (TID() == 0) block_vars_count = 0;\n  __syncthreads();\n\n  atomicAdd(&block_node_count, node_count);\n  atomicAdd(&block_vars_count, vars_count);\n  __syncthreads();\n\n  if (TID() == 0) atomicAdd(&NODE_COUNT, block_node_count);\n  if (TID() == 0) atomicAdd(&VARS_COUNT, block_vars_count);\n}\n\n__global__ void print_heatmap(GNet* gnet, u32 turn) {\n  if (GID() > 0) return;\n\n  const char* heatChars[] = {\n    //\" \", \".\", \":\", \":\",\n    //\"∴\", \"⁘\", \"⁙\", \"░\",\n    //\"░\", \"░\", \"▒\", \"▒\",\n    //\"▒\", \"▓\", \"▓\", \"▓\"\n    \" \", \"1\", \"2\", \"3\",\n    \"4\", \"5\", \"6\", \"7\",\n    \"8\", \"9\", \"A\", \"B\",\n    \"C\", \"D\", \"E\", \"F\",\n  };\n\n  for (u32 bid = 0; bid < BPG; bid++) {\n    printf(\"|\");\n    for (u32 tid = 0; tid < TPB; tid++) {\n      u32 gid = bid * TPB + tid;\n      u32 len = 0;\n      for (u32 i = 0; i < RLEN; i++) {\n        if ( turn % 2 == 0 && gnet->rbag_buf_A[gid * RLEN + i] != 0\n          || turn % 2 == 1 && gnet->rbag_buf_B[gid * RLEN + i] != 0) {\n          len++;\n        }\n      }\n      u32 pos = gnet->rbag_pos[gid];\n      u32 heat = min(len, 0xF);\n      printf(\"%s\", heatChars[heat]);\n    }\n    printf(\"|\\n\");\n  }\n}\n\n__global__ void print_result(GNet* gnet) {\n  Net net = vnet_new(gnet, NULL, gnet->turn);\n  if (threadIdx.x == 0 && blockIdx.x == 0) {\n    printf(\"Result: \");\n    pretty_print_port(&net, enter(&net, ROOT));\n    printf(\"\\n\");\n  }\n}\n\n// Demos\n// -----\n\n  // stress_test 2^10 x 65536\n  //static const u8 BOOK_BUF[] = {6, 0, 0, 0, 0, 0, 0, 0, 109, 97, 105, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 4, 0, 0, 0, 11, 10, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 102, 117, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 25, 0, 0, 0, 2, 0, 0, 0, 102, 117, 110, 95, 95, 67, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 4, 0, 0, 0, 11, 0, 0, 1, 0, 0, 0, 0, 3, 0, 0, 0, 102, 117, 110, 95, 95, 67, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 6, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 9, 0, 0, 128, 20, 0, 0, 0, 9, 0, 0, 128, 44, 0, 0, 0, 13, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 30, 0, 0, 0, 3, 4, 0, 0, 38, 0, 0, 0, 24, 0, 0, 0, 16, 0, 0, 0, 8, 0, 0, 0, 24, 0, 0, 0, 4, 0, 0, 0, 108, 111, 111, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 41, 0, 0, 0, 5, 0, 0, 0, 108, 111, 111, 112, 95, 95, 67, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 0};\n\n  // stress_test 2^18 x 65536\n  //static const u8 BOOK_BUF[] = {6, 0, 0, 0, 0, 0, 0, 0, 109, 97, 105, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 4, 0, 0, 0, 11, 18, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 102, 117, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 25, 0, 0, 0, 2, 0, 0, 0, 102, 117, 110, 95, 95, 67, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 4, 0, 0, 0, 11, 0, 0, 1, 0, 0, 0, 0, 3, 0, 0, 0, 102, 117, 110, 95, 95, 67, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 6, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 9, 0, 0, 128, 20, 0, 0, 0, 9, 0, 0, 128, 44, 0, 0, 0, 13, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 30, 0, 0, 0, 3, 4, 0, 0, 38, 0, 0, 0, 24, 0, 0, 0, 16, 0, 0, 0, 8, 0, 0, 0, 24, 0, 0, 0, 4, 0, 0, 0, 108, 111, 111, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 41, 0, 0, 0, 5, 0, 0, 0, 108, 111, 111, 112, 95, 95, 67, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 0};\n\n  // bitonic_sort 2^20\n  //static const u8 BOOK_BUF[] = {19, 0, 0, 0, 0, 0, 0, 0, 109, 97, 105, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 89, 0, 0, 0, 4, 0, 0, 0, 11, 18, 0, 0, 12, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 100, 111, 119, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 15, 0, 0, 0, 60, 0, 0, 0, 20, 0, 0, 0, 44, 0, 0, 0, 28, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 0, 36, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 52, 0, 0, 0, 16, 0, 0, 0, 24, 0, 0, 0, 16, 0, 0, 0, 68, 0, 0, 0, 8, 0, 0, 0, 24, 0, 0, 0, 2, 0, 0, 0, 100, 111, 119, 110, 95, 95, 67, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 13, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 25, 0, 0, 128, 60, 0, 0, 0, 25, 0, 0, 128, 84, 0, 0, 0, 13, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 28, 0, 0, 0, 36, 0, 0, 0, 16, 0, 0, 0, 24, 0, 0, 0, 45, 0, 0, 0, 52, 0, 0, 0, 32, 0, 0, 0, 40, 0, 0, 0, 48, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 32, 0, 0, 0, 76, 0, 0, 0, 16, 0, 0, 0, 48, 0, 0, 0, 8, 0, 0, 0, 92, 0, 0, 0, 40, 0, 0, 0, 100, 0, 0, 0, 24, 0, 0, 0, 56, 0, 0, 0, 3, 0, 0, 0, 102, 108, 111, 119, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 15, 0, 0, 0, 60, 0, 0, 0, 20, 0, 0, 0, 44, 0, 0, 0, 28, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 36, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 52, 0, 0, 0, 16, 0, 0, 0, 24, 0, 0, 0, 16, 0, 0, 0, 68, 0, 0, 0, 8, 0, 0, 0, 24, 0, 0, 0, 4, 0, 0, 0, 102, 108, 111, 119, 95, 95, 67, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 14, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 9, 0, 0, 0, 60, 0, 0, 0, 129, 0, 0, 0, 84, 0, 0, 0, 13, 0, 0, 0, 28, 0, 0, 0, 22, 0, 0, 0, 8, 0, 0, 0, 35, 1, 0, 0, 0, 0, 0, 0, 36, 0, 0, 0, 44, 0, 0, 0, 16, 0, 0, 0, 24, 0, 0, 0, 53, 0, 0, 0, 48, 0, 0, 0, 32, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 32, 0, 0, 0, 76, 0, 0, 0, 56, 0, 0, 0, 48, 0, 0, 0, 8, 0, 0, 0, 92, 0, 0, 0, 40, 0, 0, 0, 100, 0, 0, 0, 16, 0, 0, 0, 108, 0, 0, 0, 24, 0, 0, 0, 56, 0, 0, 0, 5, 0, 0, 0, 103, 101, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 2, 0, 0, 0, 4, 0, 0, 0, 15, 0, 0, 0, 8, 0, 0, 0, 20, 0, 0, 0, 8, 0, 0, 0, 28, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 103, 101, 110, 95, 95, 67, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 12, 0, 0, 0, 6, 0, 0, 0, 4, 0, 0, 0, 41, 0, 0, 128, 68, 0, 0, 0, 41, 0, 0, 128, 84, 0, 0, 0, 13, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 29, 0, 0, 0, 60, 0, 0, 0, 38, 0, 0, 0, 54, 0, 0, 0, 59, 2, 0, 0, 46, 0, 0, 0, 35, 1, 0, 0, 16, 0, 0, 0, 59, 2, 0, 0, 24, 0, 0, 0, 32, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 76, 0, 0, 0, 16, 0, 0, 0, 32, 0, 0, 0, 8, 0, 0, 0, 92, 0, 0, 0, 24, 0, 0, 0, 40, 0, 0, 0, 7, 0, 0, 0, 109, 97, 105, 110, 95, 95, 67, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 4, 0, 0, 0, 11, 18, 0, 0, 12, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 109, 97, 105, 110, 95, 95, 67, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 73, 0, 0, 0, 4, 0, 0, 0, 11, 18, 0, 0, 12, 0, 0, 0, 11, 0, 0, 0, 20, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 115, 111, 114, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 15, 0, 0, 0, 60, 0, 0, 0, 20, 0, 0, 0, 44, 0, 0, 0, 28, 0, 0, 0, 81, 0, 0, 0, 0, 0, 0, 0, 36, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 52, 0, 0, 0, 16, 0, 0, 0, 24, 0, 0, 0, 16, 0, 0, 0, 68, 0, 0, 0, 8, 0, 0, 0, 24, 0, 0, 0, 10, 0, 0, 0, 115, 111, 114, 116, 95, 95, 67, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 17, 0, 0, 0, 9, 0, 0, 0, 4, 0, 0, 0, 25, 0, 0, 0, 60, 0, 0, 0, 73, 0, 0, 128, 92, 0, 0, 0, 73, 0, 0, 128, 116, 0, 0, 0, 13, 0, 0, 0, 36, 0, 0, 0, 22, 0, 0, 0, 29, 0, 0, 0, 35, 1, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 16, 0, 0, 0, 44, 0, 0, 0, 52, 0, 0, 0, 24, 0, 0, 0, 32, 0, 0, 0, 40, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 40, 0, 0, 0, 76, 0, 0, 0, 84, 0, 0, 0, 48, 0, 0, 0, 56, 0, 0, 0, 64, 0, 0, 0, 8, 0, 0, 0, 100, 0, 0, 0, 11, 0, 0, 0, 108, 0, 0, 0, 24, 0, 0, 0, 56, 0, 0, 0, 16, 0, 0, 0, 124, 0, 0, 0, 11, 1, 0, 0, 132, 0, 0, 0, 32, 0, 0, 0, 64, 0, 0, 0, 11, 0, 0, 0, 115, 117, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 2, 0, 0, 0, 4, 0, 0, 0, 15, 0, 0, 0, 8, 0, 0, 0, 20, 0, 0, 0, 8, 0, 0, 0, 28, 0, 0, 0, 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 115, 117, 109, 95, 95, 67, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 10, 0, 0, 0, 6, 0, 0, 0, 4, 0, 0, 0, 89, 0, 0, 128, 36, 0, 0, 0, 89, 0, 0, 128, 68, 0, 0, 0, 13, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 28, 0, 0, 0, 32, 0, 0, 0, 16, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 44, 0, 0, 0, 16, 0, 0, 0, 54, 0, 0, 0, 3, 4, 0, 0, 62, 0, 0, 0, 40, 0, 0, 0, 32, 0, 0, 0, 8, 0, 0, 0, 76, 0, 0, 0, 24, 0, 0, 0, 40, 0, 0, 0, 13, 0, 0, 0, 115, 119, 97, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 15, 0, 0, 0, 44, 0, 0, 0, 20, 0, 0, 0, 28, 0, 0, 0, 113, 0, 0, 0, 121, 0, 0, 0, 0, 0, 0, 0, 36, 0, 0, 0, 8, 0, 0, 0, 16, 0, 0, 0, 8, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 14, 0, 0, 0, 115, 119, 97, 112, 95, 95, 67, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 2, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 8, 0, 0, 0, 20, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 115, 119, 97, 112, 95, 95, 67, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 2, 0, 0, 0, 4, 0, 0, 0, 2, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 8, 0, 0, 0, 28, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 16, 0, 0, 0, 119, 97, 114, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 15, 0, 0, 0, 52, 0, 0, 0, 20, 0, 0, 0, 28, 0, 0, 0, 137, 0, 0, 0, 145, 0, 0, 0, 0, 0, 0, 0, 36, 0, 0, 0, 8, 0, 0, 0, 44, 0, 0, 0, 16, 0, 0, 0, 24, 0, 0, 0, 16, 0, 0, 0, 60, 0, 0, 0, 8, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 17, 0, 0, 0, 119, 97, 114, 112, 95, 95, 67, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 12, 0, 0, 0, 6, 0, 0, 0, 4, 0, 0, 0, 105, 0, 0, 0, 76, 0, 0, 0, 13, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 29, 0, 0, 0, 52, 0, 0, 0, 38, 0, 0, 0, 24, 0, 0, 0, 3, 15, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 62, 0, 0, 0, 40, 0, 0, 0, 3, 18, 0, 0, 70, 0, 0, 0, 16, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 84, 0, 0, 0, 24, 0, 0, 0, 92, 0, 0, 0, 8, 0, 0, 0, 40, 0, 0, 0, 18, 0, 0, 0, 119, 97, 114, 112, 95, 95, 67, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 21, 0, 0, 0, 12, 0, 0, 0, 4, 0, 0, 0, 129, 0, 0, 128, 92, 0, 0, 0, 129, 0, 0, 128, 132, 0, 0, 0, 13, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 28, 0, 0, 0, 36, 0, 0, 0, 16, 0, 0, 0, 24, 0, 0, 0, 44, 0, 0, 0, 52, 0, 0, 0, 32, 0, 0, 0, 40, 0, 0, 0, 61, 0, 0, 0, 68, 0, 0, 0, 48, 0, 0, 0, 56, 0, 0, 0, 76, 0, 0, 0, 84, 0, 0, 0, 64, 0, 0, 0, 72, 0, 0, 0, 80, 0, 0, 0, 88, 0, 0, 0, 8, 0, 0, 0, 100, 0, 0, 0, 56, 0, 0, 0, 108, 0, 0, 0, 40, 0, 0, 0, 116, 0, 0, 0, 24, 0, 0, 0, 124, 0, 0, 0, 72, 0, 0, 0, 88, 0, 0, 0, 0, 0, 0, 0, 140, 0, 0, 0, 48, 0, 0, 0, 148, 0, 0, 0, 32, 0, 0, 0, 156, 0, 0, 0, 16, 0, 0, 0, 164, 0, 0, 0, 64, 0, 0, 0, 80, 0, 0, 0};\n\n//COMPILED_BOOK_BUF//\n\n// Main\n// ----\n\n#ifdef IO\nvoid do_run_io(GNet* gnet, Book* book, Port port);\n#endif\n\nextern \"C\" void hvm_cu(u32* book_buffer) {\n  // Loads the Book\n  Book* book = (Book*)malloc(sizeof(Book));\n  if (book_buffer) {\n    if (!book_load(book, (u32*)book_buffer)) {\n      fprintf(stderr, \"failed to load book\\n\");\n\n      return;\n    }\n    cudaMemcpyToSymbol(BOOK, book, sizeof(Book));\n  }\n\n  // Configures Shared Memory Size\n  cudaFuncSetAttribute(evaluator, cudaFuncAttributeMaxDynamicSharedMemorySize, sizeof(LNet));\n\n  // Creates a new GNet\n  GNet* gnet = gnet_create();\n\n  // Start the timer\n  clock_t start = clock();\n\n  // Boots root redex, to expand @main\n  gnet_boot_redex(gnet, new_pair(new_port(REF, 0), ROOT));\n\n  #ifdef IO\n  do_run_io(gnet, book, ROOT);\n  #else\n  gnet_normalize(gnet);\n  #endif\n\n  cudaDeviceSynchronize();\n\n  // Stops the timer\n  clock_t end = clock();\n  double duration = ((double)(end - start)) / CLOCKS_PER_SEC;\n\n  // Prints the result\n  print_result<<<1,1>>>(gnet);\n\n  // Reports errors\n  cudaError_t err = cudaGetLastError();\n  if (err != cudaSuccess) {\n    fprintf(stderr, \"Failed to launch kernels. Error code: %s.\\n\", cudaGetErrorString(err));\n    if (err == cudaErrorInvalidConfiguration) {\n      fprintf(stderr, \"Note: for now, HVM-CUDA requires a GPU with at least 128 KB of L1 cache per SM.\\n\");\n    }\n    exit(EXIT_FAILURE);\n  }\n\n  // Prints entire memdump\n  //{\n    //// Allocate host memory for the net\n    //GNet *h_gnet = (GNet*)malloc(sizeof(GNet));\n\n    //// Copy the net from device to host\n    //cudaMemcpy(h_gnet, gnet, sizeof(GNet), cudaMemcpyDeviceToHost);\n\n    //// Create a Net view of the host GNet\n    //Net net;\n    //net.g_node_buf = h_gnet->node_buf;\n    //net.g_vars_buf = h_gnet->vars_buf;\n\n    //// Print the net\n    //print_net(&net, L_NODE_LEN, G_NODE_LEN);\n\n    //// Free host memory\n    //free(h_gnet);\n  //}\n\n  // Gets interaction count\n  //cudaMemcpy(&itrs, &gnet->itrs, sizeof(u64), cudaMemcpyDeviceToHost);\n\n  // Prints interactions, time and MIPS\n  printf(\"- ITRS: %llu\\n\", gnet_get_itrs(gnet));\n  printf(\"- LEAK: %llu\\n\", gnet_get_leak(gnet));\n  printf(\"- TIME: %.2fs\\n\", duration);\n  printf(\"- MIPS: %.2f\\n\", (double)gnet_get_itrs(gnet) / duration / 1000000.0);\n}\n\n#ifdef WITH_MAIN\nint main() {\n  hvm_cu((u32*)BOOK_BUF);\n  return 0;\n}\n#endif\n"
  },
  {
    "path": "src/hvm.cuh",
    "content": "#ifndef hvm_cuh_INCLUDED\n#define hvm_cuh_INCLUDED\n\n#include <math.h>\n#include <stdint.h>\n\n// Types\n// -----\n\ntypedef  uint8_t u8;\ntypedef uint16_t u16;\ntypedef uint32_t u32;\ntypedef unsigned long long int u64;\ntypedef  int32_t i32;\ntypedef    float f32;\ntypedef   double f64;\n\n// Local Types\ntypedef u8  Tag;  // Tag  ::= 3-bit (rounded up to u8)\ntypedef u32 Val;  // Val  ::= 29-bit (rounded up to u32)\ntypedef u32 Port; // Port ::= Tag + Val (fits a u32)\ntypedef u64 Pair; // Pair ::= Port + Port (fits a u64)\n\n// Numbs\ntypedef u32 Numb; // Numb ::= 29-bit (rounded up to u32)\n\n// Tags\nconst Tag VAR = 0x0; // variable\nconst Tag REF = 0x1; // reference\nconst Tag ERA = 0x2; // eraser\nconst Tag NUM = 0x3; // number\nconst Tag CON = 0x4; // constructor\nconst Tag DUP = 0x5; // duplicator\nconst Tag OPR = 0x6; // operator\nconst Tag SWI = 0x7; // switch\n\n// Numbers\nstatic const f32 U24_MAX = (f32) (1 << 24) - 1;\nstatic const f32 U24_MIN = 0.0;\nstatic const f32 I24_MAX = (f32) (1 << 23) - 1;\nstatic const f32 I24_MIN = (f32) (i32) ((-1u) << 23);\nconst Tag TY_SYM = 0x00;\nconst Tag TY_U24 = 0x01;\nconst Tag TY_I24 = 0x02;\nconst Tag TY_F24 = 0x03;\nconst Tag OP_ADD = 0x04;\nconst Tag OP_SUB = 0x05;\nconst Tag FP_SUB = 0x06;\nconst Tag OP_MUL = 0x07;\nconst Tag OP_DIV = 0x08;\nconst Tag FP_DIV = 0x09;\nconst Tag OP_REM = 0x0A;\nconst Tag FP_REM = 0x0B;\nconst Tag OP_EQ  = 0x0C;\nconst Tag OP_NEQ = 0x0D;\nconst Tag OP_LT  = 0x0E;\nconst Tag OP_GT  = 0x0F;\nconst Tag OP_AND = 0x10;\nconst Tag OP_OR  = 0x11;\nconst Tag OP_XOR = 0x12;\nconst Tag OP_SHL = 0x13;\nconst Tag FP_SHL = 0x14;\nconst Tag OP_SHR = 0x15;\nconst Tag FP_SHR = 0x16;\n\ntypedef struct GNet GNet;\n\n// Debugger\n// --------\n\n// Port: Constructor and Getters\n// -----------------------------\n\nstatic inline Port new_port(Tag tag, Val val) {\n  return (val << 3) | tag;\n}\n\nstatic inline Tag get_tag(Port port) {\n  return port & 7;\n}\n\nstatic inline Val get_val(Port port) {\n  return port >> 3;\n}\n\n// Pair: Constructor and Getters\n// -----------------------------\n\nstatic inline const Pair new_pair(Port fst, Port snd) {\n  return ((u64)snd << 32) | fst;\n}\n\nstatic inline Port get_fst(Pair pair) {\n  return pair & 0xFFFFFFFF;\n}\n\nstatic inline Port get_snd(Pair pair) {\n  return pair >> 32;\n}\n\n// Utils\n// -----\n\n// Swaps two ports.\nstatic inline void swap(Port *a, Port *b) {\n  Port x = *a; *a = *b; *b = x;\n}\n\nstatic inline u32 min(u32 a, u32 b) {\n  return (a < b) ? a : b;\n}\n\nstatic inline f32 clamp(f32 x, f32 min, f32 max) {\n  const f32 t = x < min ? min : x;\n  return (t > max) ? max : t;\n}\n\n// Numbs\n// -----\n\n// Constructor and getters for SYM (operation selector)\nstatic inline Numb new_sym(u32 val) {\n  return (val << 5) | TY_SYM;\n}\n\nstatic inline u32 get_sym(Numb word) {\n  return (word >> 5);\n}\n\n// Constructor and getters for U24 (unsigned 24-bit integer)\nstatic inline Numb new_u24(u32 val) {\n  return (val << 5) | TY_U24;\n}\n\nstatic inline u32 get_u24(Numb word) {\n  return word >> 5;\n}\n\n// Constructor and getters for I24 (signed 24-bit integer)\nstatic inline Numb new_i24(i32 val) {\n  return ((u32)val << 5) | TY_I24;\n}\n\nstatic inline i32 get_i24(Numb word) {\n  return ((i32)word) << 3 >> 8;\n}\n\n// Constructor and getters for F24 (24-bit float)\nstatic inline Numb new_f24(float val) {\n  u32 bits = *(u32*)&val;\n  u32 shifted_bits = bits >> 8;\n  u32 lost_bits = bits & 0xFF;\n  // round ties to even\n  shifted_bits += (!isnan(val)) & ((lost_bits - ((lost_bits >> 7) & !shifted_bits)) >> 7);\n  // ensure NaNs don't become infinities\n  shifted_bits |= isnan(val);\n  return (shifted_bits << 5) | TY_F24;\n}\n\nstatic inline float get_f24(Numb word) {\n  u32 bits = (word << 3) & 0xFFFFFF00;\n  return *(float*)&bits;\n}\n\nstatic inline Tag get_typ(Numb word) {\n  return word & 0x1F;\n}\n\nstatic inline bool is_num(Numb word) {\n  return get_typ(word) >= TY_U24 && get_typ(word) <= TY_F24;\n}\n\nstatic inline bool is_cast(Numb word) {\n  return get_typ(word) == TY_SYM && get_sym(word) >= TY_U24 && get_sym(word) <= TY_F24;\n}\n\n// Partial application\nstatic inline Numb partial(Numb a, Numb b) {\n  return (b & ~0x1F) | get_sym(a);\n}\n\n// Readback\n// ---------\n\n// Readback: Tuples\ntypedef struct Tup {\n  u32  elem_len;\n  Port elem_buf[8];\n} Tup;\n\n// Reads a tuple of `size` elements from `port`.\n// Tuples are con nodes nested to the right auxilliary port,\n// For example, `(CON a (CON b (CON c)))` is a 3-tuple (a, b, c).\nextern Tup gnet_readback_tup(GNet* gnet, Port port, u32 size);\n\ntypedef struct Str {\n  u32  len;\n  char *buf;\n} Str;\n\n// Reads a constructor-encoded string (of length at most 255 characters),\n// into a null-terminated `Str`.\nextern Str gnet_readback_str(GNet* gnet, Port port);\n\ntypedef struct Bytes {\n  u32  len;\n  char *buf;\n} Bytes;\n\n// Reads a constructor-encoded string (of length at most 256 characters),\n// into a `Bytes`. The returned `Bytes` is not null terminated.\nextern Bytes gnet_readback_bytes(GNet* net, Port port);\n\n// Creates a construtor-encoded string of arbitrary length from the\n// provided `bytes`. This string can be consumed on the HVM-side. This\n// will return an `ERA` if nodes cannot be allocated.\nextern Port gnet_inject_bytes(GNet* net, Bytes *bytes);\n\n#endif // hvm_cuh_INCLUDED\n"
  },
  {
    "path": "src/hvm.h",
    "content": "#ifndef hvm_h_INCLUDED\n#define hvm_h_INCLUDED\n\n#include <math.h>\n#include <stdint.h>\n#include <stdbool.h>\n\n// Types\n// -----\n\ntypedef  uint8_t  u8;\ntypedef uint16_t u16;\ntypedef uint32_t u32;\ntypedef  int32_t i32;\ntypedef uint64_t u64;\ntypedef    float f32;\ntypedef   double f64;\n\n// Local Types\ntypedef u8  Tag;  // Tag  ::= 3-bit (rounded up to u8)\ntypedef u32 Val;  // Val  ::= 29-bit (rounded up to u32)\ntypedef u32 Port; // Port ::= Tag + Val (fits a u32)\ntypedef u64 Pair; // Pair ::= Port + Port (fits a u64)\n\n// Numbs\ntypedef u32 Numb; // Numb ::= 29-bit (rounded up to u32)\n\n// Tags\n#define VAR 0x0 // variable\n#define REF 0x1 // reference\n#define ERA 0x2 // eraser\n#define NUM 0x3 // number\n#define CON 0x4 // constructor\n#define DUP 0x5 // duplicator\n#define OPR 0x6 // operator\n#define SWI 0x7 // switch\n\n// Numbers\nstatic const f32 U24_MAX = (f32) (1 << 24) - 1;\nstatic const f32 U24_MIN = 0.0;\nstatic const f32 I24_MAX = (f32) (1 << 23) - 1;\nstatic const f32 I24_MIN = (f32) (i32) ((-1u) << 23);\n#define TY_SYM 0x00\n#define TY_U24 0x01\n#define TY_I24 0x02\n#define TY_F24 0x03\n#define OP_ADD 0x04\n#define OP_SUB 0x05\n#define FP_SUB 0x06\n#define OP_MUL 0x07\n#define OP_DIV 0x08\n#define FP_DIV 0x09\n#define OP_REM 0x0A\n#define FP_REM 0x0B\n#define OP_EQ  0x0C\n#define OP_NEQ 0x0D\n#define OP_LT  0x0E\n#define OP_GT  0x0F\n#define OP_AND 0x10\n#define OP_OR  0x11\n#define OP_XOR 0x12\n#define OP_SHL 0x13\n#define FP_SHL 0x14\n#define OP_SHR 0x15\n#define FP_SHR 0x16\n\ntypedef struct Net Net;\ntypedef struct Book Book;\n\n// Debugger\n// --------\n\ntypedef struct {\n  char x[13];\n} Show;\n\nvoid put_u16(char* B, u16 val);\nShow show_port(Port port);\nvoid print_net(Net* net);\nvoid pretty_print_numb(Numb word);\nvoid pretty_print_port(Net* net, Book* book, Port port);\n\n// Port: Constructor and Getters\n// -----------------------------\n\nstatic inline Port new_port(Tag tag, Val val) {\n  return (val << 3) | tag;\n}\n\nstatic inline Tag get_tag(Port port) {\n  return port & 7;\n}\n\nstatic inline Val get_val(Port port) {\n  return port >> 3;\n}\n\n// Pair: Constructor and Getters\n// -----------------------------\n\nstatic inline const Pair new_pair(Port fst, Port snd) {\n  return ((u64)snd << 32) | fst;\n}\n\nstatic inline Port get_fst(Pair pair) {\n  return pair & 0xFFFFFFFF;\n}\n\nstatic inline Port get_snd(Pair pair) {\n  return pair >> 32;\n}\n\n// Utils\n// -----\n\n// Swaps two ports.\nstatic inline void swap(Port *a, Port *b) {\n  Port x = *a; *a = *b; *b = x;\n}\n\nstatic inline u32 min(u32 a, u32 b) {\n  return (a < b) ? a : b;\n}\n\nstatic inline f32 clamp(f32 x, f32 min, f32 max) {\n  const f32 t = x < min ? min : x;\n  return (t > max) ? max : t;\n}\n\n// Numbs\n// -----\n\n// Constructor and getters for SYM (operation selector)\nstatic inline Numb new_sym(u32 val) {\n  return (val << 5) | TY_SYM;\n}\n\nstatic inline u32 get_sym(Numb word) {\n  return (word >> 5);\n}\n\n// Constructor and getters for U24 (unsigned 24-bit integer)\nstatic inline Numb new_u24(u32 val) {\n  return (val << 5) | TY_U24;\n}\n\nstatic inline u32 get_u24(Numb word) {\n  return word >> 5;\n}\n\n// Constructor and getters for I24 (signed 24-bit integer)\nstatic inline Numb new_i24(i32 val) {\n  return ((u32)val << 5) | TY_I24;\n}\n\nstatic inline i32 get_i24(Numb word) {\n  return ((i32)word) << 3 >> 8;\n}\n\n// Constructor and getters for F24 (24-bit float)\nstatic inline Numb new_f24(float val) {\n  u32 bits = *(u32*)&val;\n  u32 shifted_bits = bits >> 8;\n  u32 lost_bits = bits & 0xFF;\n  // round ties to even\n  shifted_bits += (!isnan(val)) & ((lost_bits - ((lost_bits >> 7) & !shifted_bits)) >> 7);\n  // ensure NaNs don't become infinities\n  shifted_bits |= isnan(val);\n  return (shifted_bits << 5) | TY_F24;\n}\n\nstatic inline float get_f24(Numb word) {\n  u32 bits = (word << 3) & 0xFFFFFF00;\n  return *(float*)&bits;\n}\n\nstatic inline Tag get_typ(Numb word) {\n  return word & 0x1F;\n}\n\nstatic inline bool is_num(Numb word) {\n  return get_typ(word) >= TY_U24 && get_typ(word) <= TY_F24;\n}\n\nstatic inline bool is_cast(Numb word) {\n  return get_typ(word) == TY_SYM && get_sym(word) >= TY_U24 && get_sym(word) <= TY_F24;\n}\n\n// Partial application\nstatic inline Numb partial(Numb a, Numb b) {\n  return (b & ~0x1F) | get_sym(a);\n}\n\n// Readback\n// ---------\n\n// Readback: Tuples\ntypedef struct Tup {\n  u32  elem_len;\n  Port elem_buf[8];\n} Tup;\n\n// Reads a tuple of `size` elements from `port`.\n// Tuples are con nodes nested to the right auxilliary port,\n// For example, `(CON a (CON b (CON c)))` is a 3-tuple (a, b, c).\nextern Tup readback_tup(Net* net, Book* book, Port port, u32 size);\n\ntypedef struct Str {\n  u32  len;\n  char *buf;\n} Str;\n\n// Reads a constructor-encoded string (of length at most 255 characters),\n// into a null-terminated `Str`.\nextern Str readback_str(Net* net, Book* book, Port port);\n\ntypedef struct Bytes {\n  u32  len;\n  char *buf;\n} Bytes;\n\n// Reads a constructor-encoded string (of length at most 256 characters),\n// into a `Bytes`. The returned `Bytes` is not null terminated.\nextern Bytes readback_bytes(Net* net, Book* book, Port port);\n\n// Creates a construtor-encoded string of arbitrary length from the\n// provided `bytes`. This string can be consumed on the HVM-side. This\n// will return an `ERA` if nodes cannot be allocated.\nextern Port inject_bytes(Net* net, Bytes *bytes);\n\n#endif // hvm_h_INCLUDED\n"
  },
  {
    "path": "src/hvm.rs",
    "content": "use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};\nuse std::alloc::{alloc, dealloc, Layout};\nuse std::mem;\n\n// Runtime\n// =======\n\n// Types\npub type Tag  = u8;  // Tag  ::= 3-bit (rounded up to u8)\npub type Lab  = u32; // Lab  ::= 29-bit (rounded up to u32)\npub type Val  = u32; // Val  ::= 29-bit (rounded up to u32)\npub type Rule = u8;  // Rule ::= 8-bit (fits a u8)\n\n// Port\n#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)]\npub struct Port(pub Val);\n\n// Pair\npub struct Pair(pub u64);\n\n// Atomics\npub type AVal = AtomicU32;\npub struct APort(pub AVal);\npub struct APair(pub AtomicU64);\n\n// Number\npub struct Numb(pub Val);\nconst U24_MAX : u32 = (1 << 24) - 1;\nconst U24_MIN : u32 = 0;\nconst I24_MAX : i32 = (1 << 23) - 1;\nconst I24_MIN : i32 = (-1) << 23;\n\n// Tags\npub const VAR : Tag = 0x0; // variable\npub const REF : Tag = 0x1; // reference\npub const ERA : Tag = 0x2; // eraser\npub const NUM : Tag = 0x3; // number\npub const CON : Tag = 0x4; // constructor\npub const DUP : Tag = 0x5; // duplicator\npub const OPR : Tag = 0x6; // operator\npub const SWI : Tag = 0x7; // switch\n\n// Rules\npub const LINK : Rule = 0x0;\npub const CALL : Rule = 0x1;\npub const VOID : Rule = 0x2;\npub const ERAS : Rule = 0x3;\npub const ANNI : Rule = 0x4;\npub const COMM : Rule = 0x5;\npub const OPER : Rule = 0x6;\npub const SWIT : Rule = 0x7;\n\n// Numbs\npub const TY_SYM : Tag = 0x00;\npub const TY_U24 : Tag = 0x01;\npub const TY_I24 : Tag = 0x02;\npub const TY_F24 : Tag = 0x03;\npub const OP_ADD : Tag = 0x04;\npub const OP_SUB : Tag = 0x05;\npub const FP_SUB : Tag = 0x06;\npub const OP_MUL : Tag = 0x07;\npub const OP_DIV : Tag = 0x08;\npub const FP_DIV : Tag = 0x09;\npub const OP_REM : Tag = 0x0A;\npub const FP_REM : Tag = 0x0B;\npub const OP_EQ  : Tag = 0x0C;\npub const OP_NEQ : Tag = 0x0D;\npub const OP_LT  : Tag = 0x0E;\npub const OP_GT  : Tag = 0x0F;\npub const OP_AND : Tag = 0x10;\npub const OP_OR  : Tag = 0x11;\npub const OP_XOR : Tag = 0x12;\npub const OP_SHL : Tag = 0x13;\npub const FP_SHL : Tag = 0x14;\npub const OP_SHR : Tag = 0x15;\npub const FP_SHR : Tag = 0x16;\n\n// Constants\npub const FREE : Port = Port(0x0);\npub const ROOT : Port = Port(0xFFFFFFF8);\npub const NONE : Port = Port(0xFFFFFFFF);\n\n// RBag\npub struct RBag {\n  pub lo: Vec<Pair>,\n  pub hi: Vec<Pair>,\n}\n\n// Global Net\npub struct GNet<'a> {\n  pub nlen: usize, // length of the node buffer\n  pub vlen: usize, // length of the vars buffer\n  pub node: &'a mut [APair], // node buffer\n  pub vars: &'a mut [APort], // vars buffer\n  pub itrs: AtomicU64, // interaction count\n}\n\n// Thread Memory\npub struct TMem {\n  pub tid: u32, // thread id\n  pub tids: u32, // thread count\n  pub tick: u32, // tick counter\n  pub itrs: u32, // interaction count\n  pub nput: usize, // next node allocation index\n  pub vput: usize, // next vars allocation index\n  pub nloc: Vec<usize>, // allocated node locations\n  pub vloc: Vec<usize>, // allocated vars locations\n  pub rbag: RBag, // local redex bag\n}\n\n// Top-Level Definition\npub struct Def {\n  pub name: String, // def name\n  pub safe: bool, // has no dups\n  pub root: Port, // root port\n  pub rbag: Vec<Pair>, // def redex bag\n  pub node: Vec<Pair>, // def node buffer\n  pub vars: usize, // def vars count\n}\n\n// Book of Definitions\npub struct Book {\n  pub defs: Vec<Def>,\n}\n\nimpl Port {\n  pub fn new(tag: Tag, val: Val) -> Self {\n    Port((val << 3) | tag as Val)\n  }\n\n  pub fn get_tag(&self) -> Tag {\n    (self.0 & 7) as Tag\n  }\n\n  pub fn get_val(&self) -> Val {\n    self.0 >> 3\n  }\n\n  pub fn is_nod(&self) -> bool {\n    self.get_tag() >= CON\n  }\n\n  pub fn is_var(&self) -> bool {\n    self.get_tag() == VAR\n  }\n\n  pub fn get_rule(a: Port, b: Port) -> Rule {\n    const TABLE: [[Rule; 8]; 8] = [\n      //VAR  REF  ERA  NUM  CON  DUP  OPR  SWI\n      [LINK,LINK,LINK,LINK,LINK,LINK,LINK,LINK], // VAR\n      [LINK,VOID,VOID,VOID,CALL,CALL,CALL,CALL], // REF\n      [LINK,VOID,VOID,VOID,ERAS,ERAS,ERAS,ERAS], // ERA\n      [LINK,VOID,VOID,VOID,ERAS,ERAS,OPER,SWIT], // NUM\n      [LINK,CALL,ERAS,ERAS,ANNI,COMM,COMM,COMM], // CON\n      [LINK,CALL,ERAS,ERAS,COMM,ANNI,COMM,COMM], // DUP\n      [LINK,CALL,ERAS,OPER,COMM,COMM,ANNI,COMM], // OPR\n      [LINK,CALL,ERAS,SWIT,COMM,COMM,COMM,ANNI], // SWI\n    ];\n    return TABLE[a.get_tag() as usize][b.get_tag() as usize];\n  }\n\n  pub fn should_swap(a: Port, b: Port) -> bool {\n    b.get_tag() < a.get_tag()\n  }\n\n  pub fn is_high_priority(rule: Rule) -> bool {\n    (0b00011101 >> rule) & 1 != 0\n  }\n\n  pub fn adjust_port(&self, tm: &TMem) -> Port {\n    let tag = self.get_tag();\n    let val = self.get_val();\n    if self.is_nod() {\n      Port::new(tag, tm.nloc[val as usize] as u32)\n    } else if self.is_var() {\n      Port::new(tag, tm.vloc[val as usize] as u32)\n    } else {\n      Port::new(tag, val)\n    }\n  }\n\n}\n\nimpl Pair {\n  pub fn new(fst: Port, snd: Port) -> Self {\n    Pair(((snd.0 as u64) << 32) | fst.0 as u64)\n  }\n\n  pub fn get_fst(&self) -> Port {\n    Port((self.0 & 0xFFFFFFFF) as u32)\n  }\n\n  pub fn get_snd(&self) -> Port {\n    Port((self.0 >> 32) as u32)\n  }\n\n  pub fn adjust_pair(&self, tm: &TMem) -> Pair {\n    let p1 = self.get_fst().adjust_port(tm);\n    let p2 = self.get_snd().adjust_port(tm);\n    Pair::new(p1, p2)\n  }\n\n  pub fn set_par_flag(&self) -> Self {\n    let p1 : Port = self.get_fst();\n    let p2 : Port = self.get_snd();\n    if p1.get_tag() == REF {\n      return Pair::new(Port::new(p1.get_tag(), p1.get_val() | 0x10000000), p2);\n    } else {\n      return Pair::new(p1, p2);\n    }\n  }\n\n  pub fn get_par_flag(&self) -> bool {\n    let p1 : Port = self.get_fst();\n    if p1.get_tag() == REF {\n      return p1.get_val() >> 28 == 1;\n    } else {\n      return false;\n    }\n  }\n}\n\nimpl Numb {\n\n  // SYM: a symbolic operator\n\n  pub fn new_sym(val: Tag) -> Self {\n    Numb((val as Val) << 5 | (TY_SYM as Val))\n  }\n\n  pub fn get_sym(&self) -> Tag {\n    (self.0 >> 5) as Tag\n  }\n\n  // U24: unsigned 24-bit integer\n\n  pub fn new_u24(val: u32) -> Self {\n    Numb((val << 5) as Val | (TY_U24 as Val))\n  }\n\n  pub fn get_u24(&self) -> u32 {\n    (self.0 >> 5) as u32\n  }\n\n  // I24: signed 24-bit integer\n\n  pub fn new_i24(val: i32) -> Self {\n    Numb(((val as u32) << 5) as Val | (TY_I24 as Val))\n  }\n\n  pub fn get_i24(&self) -> i32 {\n    (self.0 as i32) << 3 >> 8\n  }\n\n  // F24: 24-bit float\n\n  pub fn new_f24(val: f32) -> Self {\n    let bits = val.to_bits();\n    let mut shifted_bits = bits >> 8;\n    let lost_bits = bits & 0xFF;\n    // round ties to even\n    shifted_bits += u32::from(!val.is_nan()) & ((lost_bits - ((lost_bits >> 7) & !shifted_bits)) >> 7);\n    // ensure NaNs don't become infinities\n    shifted_bits |= u32::from(val.is_nan());\n    Numb((shifted_bits << 5) as Val | (TY_F24 as Val))\n  }\n\n  pub fn get_f24(&self) -> f32 {\n    f32::from_bits((self.0 << 3) & 0xFFFFFF00)\n  }\n\n  // Gets the numeric type.\n  pub fn get_typ(&self) -> Tag {\n    (self.0 & 0x1F) as Tag\n  }\n\n  pub fn is_num(&self) -> bool {\n    self.get_typ() >= TY_U24 && self.get_typ() <= TY_F24\n  }\n\n  pub fn is_cast(&self) -> bool {\n    self.get_typ() == TY_SYM && self.get_sym() >= TY_U24 && self.get_sym() <= TY_F24\n  }\n\n  // Partial application.\n  pub fn partial(a: Self, b: Self) -> Self {\n    Numb((b.0 & !0x1F) | a.get_sym() as u32)\n  }\n\n  // Cast a number to another type.\n  // The semantics are meant to spiritually resemble rust's numeric casts:\n  // - i24 <-> u24: is just reinterpretation of bits\n  // - f24  -> i24,\n  //   f24  -> u24: casts to the \"closest\" integer representing this float,\n  //                saturating if out of range and 0 if NaN\n  // - i24  -> f24,\n  //   u24  -> f24: casts to the \"closest\" float representing this integer.\n  pub fn cast(a: Self, b: Self) -> Self {\n    match (a.get_sym(), b.get_typ()) {\n      (TY_U24, TY_U24) => b,\n      (TY_U24, TY_I24) => Self::new_u24(b.get_i24() as u32),\n      (TY_U24, TY_F24) => Self::new_u24((b.get_f24() as u32).clamp(U24_MIN, U24_MAX)),\n\n      (TY_I24, TY_U24) => Self::new_i24(b.get_u24() as i32),\n      (TY_I24, TY_I24) => b,\n      (TY_I24, TY_F24) => Self::new_i24((b.get_f24() as i32).clamp(I24_MIN, I24_MAX)),\n\n      (TY_F24, TY_U24) => Self::new_f24(b.get_u24() as f32),\n      (TY_F24, TY_I24) => Self::new_f24(b.get_i24() as f32),\n      (TY_F24, TY_F24) => b,\n      // invalid cast\n      (_, _) => Self::new_u24(0),\n    }\n  }\n\n  pub fn operate(a: Self, b: Self) -> Self {\n    //println!(\"operate {} {}\", crate::ast::Numb(a.0).show(), crate::ast::Numb(b.0).show());\n    let at = a.get_typ();\n    let bt = b.get_typ();\n    if at == TY_SYM && bt == TY_SYM {\n      return Numb::new_u24(0);\n    }\n    if a.is_cast() && b.is_num() {\n      return Numb::cast(a, b);\n    }\n    if b.is_cast() && a.is_num() {\n      return Numb::cast(b, a);\n    }\n    if at == TY_SYM && bt != TY_SYM {\n      return Numb::partial(a, b);\n    }\n    if at != TY_SYM && bt == TY_SYM {\n      return Numb::partial(b, a);\n    }\n    if at >= OP_ADD && bt >= OP_ADD {\n      return Numb::new_u24(0);\n    }\n    if at < OP_ADD && bt < OP_ADD {\n      return Numb::new_u24(0);\n    }\n    let (op, a, ty, b) = if at >= OP_ADD { (at, a, bt, b) } else { (bt, b, at, a) };\n    match ty {\n      TY_U24 => {\n        let av = a.get_u24();\n        let bv = b.get_u24();\n        match op {\n          OP_ADD => Numb::new_u24(av.wrapping_add(bv)),\n          OP_SUB => Numb::new_u24(av.wrapping_sub(bv)),\n          FP_SUB => Numb::new_u24(bv.wrapping_sub(av)),\n          OP_MUL => Numb::new_u24(av.wrapping_mul(bv)),\n          OP_DIV => Numb::new_u24(av.wrapping_div(bv)),\n          FP_DIV => Numb::new_u24(bv.wrapping_div(av)),\n          OP_REM => Numb::new_u24(av.wrapping_rem(bv)),\n          FP_REM => Numb::new_u24(bv.wrapping_rem(av)),\n          OP_EQ  => Numb::new_u24((av == bv) as u32),\n          OP_NEQ => Numb::new_u24((av != bv) as u32),\n          OP_LT  => Numb::new_u24((av <  bv) as u32),\n          OP_GT  => Numb::new_u24((av >  bv) as u32),\n          OP_AND => Numb::new_u24(av & bv),\n          OP_OR  => Numb::new_u24(av | bv),\n          OP_XOR => Numb::new_u24(av ^ bv),\n          OP_SHL => Numb::new_u24(av << (bv & 31)),\n          OP_SHR => Numb::new_u24(av >> (bv & 31)),\n          FP_SHL => Numb::new_u24(bv << (av & 31)),\n          FP_SHR => Numb::new_u24(bv >> (av & 31)),\n          _      => unreachable!(),\n        }\n      }\n      TY_I24 => {\n        let av = a.get_i24();\n        let bv = b.get_i24();\n        match op {\n          OP_ADD => Numb::new_i24(av.wrapping_add(bv)),\n          OP_SUB => Numb::new_i24(av.wrapping_sub(bv)),\n          FP_SUB => Numb::new_i24(bv.wrapping_sub(av)),\n          OP_MUL => Numb::new_i24(av.wrapping_mul(bv)),\n          OP_DIV => Numb::new_i24(av.wrapping_div(bv)),\n          FP_DIV => Numb::new_i24(bv.wrapping_div(av)),\n          OP_REM => Numb::new_i24(av.wrapping_rem(bv)),\n          FP_REM => Numb::new_i24(bv.wrapping_rem(av)),\n          OP_EQ  => Numb::new_u24((av == bv) as u32),\n          OP_NEQ => Numb::new_u24((av != bv) as u32),\n          OP_LT  => Numb::new_u24((av <  bv) as u32),\n          OP_GT  => Numb::new_u24((av >  bv) as u32),\n          OP_AND => Numb::new_i24(av & bv),\n          OP_OR  => Numb::new_i24(av | bv),\n          OP_XOR => Numb::new_i24(av ^ bv),\n          _      => unreachable!(),\n        }\n      }\n      TY_F24 => {\n        let av = a.get_f24();\n        let bv = b.get_f24();\n        match op {\n          OP_ADD => Numb::new_f24(av + bv),\n          OP_SUB => Numb::new_f24(av - bv),\n          FP_SUB => Numb::new_f24(bv - av),\n          OP_MUL => Numb::new_f24(av * bv),\n          OP_DIV => Numb::new_f24(av / bv),\n          FP_DIV => Numb::new_f24(bv / av),\n          OP_REM => Numb::new_f24(av % bv),\n          FP_REM => Numb::new_f24(bv % av),\n          OP_EQ  => Numb::new_u24((av == bv) as u32),\n          OP_NEQ => Numb::new_u24((av != bv) as u32),\n          OP_LT  => Numb::new_u24((av <  bv) as u32),\n          OP_GT  => Numb::new_u24((av >  bv) as u32),\n          OP_AND => Numb::new_f24(av.atan2(bv)),\n          OP_OR  => Numb::new_f24(bv.log(av)),\n          OP_XOR => Numb::new_f24(av.powf(bv)),\n          OP_SHL => Numb::new_f24((av + bv).sin()),\n          OP_SHR => Numb::new_f24((av + bv).tan()),\n          _      => unreachable!(),\n        }\n      }\n      _ => Numb::new_u24(0),\n    }\n  }\n}\n\nimpl RBag {\n  pub fn new() -> Self {\n    RBag {\n      lo: Vec::new(),\n      hi: Vec::new(),\n    }\n  }\n\n  pub fn push_redex(&mut self, redex: Pair) {\n    let rule = Port::get_rule(redex.get_fst(), redex.get_snd());\n    if Port::is_high_priority(rule) {\n      self.hi.push(redex);\n    } else {\n      self.lo.push(redex);\n    }\n  }\n\n  pub fn pop_redex(&mut self) -> Option<Pair> {\n    if !self.hi.is_empty() {\n      self.hi.pop()\n    } else {\n      self.lo.pop()\n    }\n  }\n\n  pub fn len(&self) -> usize {\n    self.lo.len() + self.hi.len()\n  }\n\n  pub fn has_highs(&self) -> bool {\n    !self.hi.is_empty()\n  }\n}\n\nimpl<'a> GNet<'a> {\n  pub fn new(nlen: usize, vlen: usize) -> Self {\n    let nlay = Layout::array::<APair>(nlen).unwrap();\n    let vlay = Layout::array::<APort>(vlen).unwrap();\n    let nptr = unsafe { alloc(nlay) as *mut APair };\n    let vptr = unsafe { alloc(vlay) as *mut APort };\n    let node = unsafe { std::slice::from_raw_parts_mut(nptr, nlen) };\n    let vars = unsafe { std::slice::from_raw_parts_mut(vptr, vlen) };\n    GNet { nlen, vlen, node, vars, itrs: AtomicU64::new(0) }\n  }\n\n  pub fn node_create(&self, loc: usize, val: Pair) {\n    self.node[loc].0.store(val.0, Ordering::Relaxed);\n  }\n\n  pub fn vars_create(&self, var: usize, val: Port) {\n    self.vars[var].0.store(val.0, Ordering::Relaxed);\n  }\n\n  pub fn node_load(&self, loc: usize) -> Pair {\n    Pair(self.node[loc].0.load(Ordering::Relaxed))\n  }\n\n  pub fn vars_load(&self, var: usize) -> Port {\n    Port(self.vars[var].0.load(Ordering::Relaxed) as u32)\n  }\n\n  pub fn node_store(&self, loc: usize, val: Pair) {\n    self.node[loc].0.store(val.0, Ordering::Relaxed);\n  }\n\n  pub fn vars_store(&self, var: usize, val: Port) {\n    self.vars[var].0.store(val.0, Ordering::Relaxed);\n  }\n\n  pub fn node_exchange(&self, loc: usize, val: Pair) -> Pair {\n    Pair(self.node[loc].0.swap(val.0, Ordering::Relaxed))\n  }\n\n  pub fn vars_exchange(&self, var: usize, val: Port) -> Port {\n    Port(self.vars[var].0.swap(val.0, Ordering::Relaxed) as u32)\n  }\n\n  pub fn node_take(&self, loc: usize) -> Pair {\n    self.node_exchange(loc, Pair(0))\n  }\n\n  pub fn vars_take(&self, var: usize) -> Port {\n    self.vars_exchange(var, Port(0))\n  }\n\n  pub fn is_node_free(&self, loc: usize) -> bool {\n    self.node_load(loc).0 == 0\n  }\n\n  pub fn is_vars_free(&self, var: usize) -> bool {\n    self.vars_load(var).0 == 0\n  }\n\n  pub fn enter(&self, mut var: Port) -> Port {\n    // While `B` is VAR: extend it (as an optimization)\n    while var.get_tag() == VAR {\n      // Takes the current `B` substitution as `B'`\n      let val = self.vars_exchange(var.get_val() as usize, NONE);\n      // If there was no `B'`, stop, as there is no extension\n      if val == NONE || val == Port(0) {\n        break;\n      }\n      // Otherwise, delete `B` (we own both) and continue as `A ~> B'`\n      self.vars_take(var.get_val() as usize);\n      var = val;\n    }\n    return var;\n  }\n\n}\n\nimpl<'a> Drop for GNet<'a> {\n  fn drop(&mut self) {\n    let nlay = Layout::array::<APair>(self.nlen).unwrap();\n    let vlay = Layout::array::<APair>(self.vlen).unwrap();\n    unsafe {\n      dealloc(self.node.as_mut_ptr() as *mut u8, nlay);\n      dealloc(self.vars.as_mut_ptr() as *mut u8, vlay);\n    }\n  }\n\n}\n\nimpl TMem {\n  // TODO: implement a TMem::new() fn\n  pub fn new(tid: u32, tids: u32) -> Self {\n    TMem {\n      tid,\n      tids,\n      tick: 0,\n      itrs: 0,\n      nput: 0,\n      vput: 0,\n      nloc: vec![0; 0xFFF], // FIXME: move to a constant\n      vloc: vec![0; 0xFFF],\n      rbag: RBag::new(),\n    }\n  }\n\n  pub fn node_alloc(&mut self, net: &GNet, num: usize) -> usize {\n    let mut got = 0;\n    for _ in 0..net.nlen {\n      self.nput += 1; // index 0 reserved\n      if self.nput < net.nlen-1 || net.is_node_free(self.nput % net.nlen) {\n        self.nloc[got] = self.nput % net.nlen;\n        got += 1;\n        //println!(\"ALLOC NODE {} {}\", got, self.nput);\n      }\n      if got >= num {\n        break;\n      }\n    }\n    return got\n  }\n\n  pub fn vars_alloc(&mut self, net: &GNet, num: usize) -> usize {\n    let mut got = 0;\n    for _ in 0..net.vlen {\n      self.vput += 1; // index 0 reserved for FREE\n      if self.vput < net.vlen-1 || net.is_vars_free(self.vput % net.vlen) {\n        self.vloc[got] = self.vput % net.nlen;\n        //println!(\"ALLOC VARS {} {}\", got, self.vput);\n        got += 1;\n      }\n      if got >= num {\n        break;\n      }\n    }\n    got\n  }\n\n  pub fn get_resources(&mut self, net: &GNet, _need_rbag: usize, need_node: usize, need_vars: usize) -> bool {\n    let got_node = self.node_alloc(net, need_node);\n    let got_vars = self.vars_alloc(net, need_vars);\n    got_node >= need_node && got_vars >= need_vars\n  }\n\n  // Atomically Links `A ~ B`.\n  pub fn link(&mut self, net: &GNet, a: Port, b: Port) {\n    //println!(\"link {} ~ {}\", a.show(), b.show());\n    let mut a = a;\n    let mut b = b;\n\n    // Attempts to directionally point `A ~> B`\n    loop {\n      // If `A` is NODE: swap `A` and `B`, and continue\n      if a.get_tag() != VAR && b.get_tag() == VAR {\n        let x = a; a = b; b = x;\n      }\n\n      // If `A` is NODE: create the `A ~ B` redex\n      if a.get_tag() != VAR {\n        self.rbag.push_redex(Pair::new(a, b));\n        break;\n      }\n\n      // While `B` is VAR: extend it (as an optimization)\n      b = net.enter(b);\n\n      // Since `A` is VAR: point `A ~> B`.\n      if true {\n        // Stores `A -> B`, taking the current `A` subst as `A'`\n        let a_ = net.vars_exchange(a.get_val() as usize, b);\n        // If there was no `A'`, stop, as we lost B's ownership\n        if a_ == NONE {\n          break;\n        }\n        // Otherwise, delete `A` (we own both) and link `A' ~ B`\n        net.vars_take(a.get_val() as usize);\n        a = a_;\n      }\n    }\n  }\n\n  // Links `A ~ B` (as a pair).\n  pub fn link_pair(&mut self, net: &GNet, ab: Pair) {\n    self.link(net, ab.get_fst(), ab.get_snd());\n    //println!(\"link_pair {:016X}\", ab.0);\n  }\n\n  // The Link Interaction.\n  pub fn interact_link(&mut self, net: &GNet, a: Port, b: Port) -> bool {\n    // Allocates needed nodes and vars.\n    if !self.get_resources(net, 1, 0, 0) {\n      return false;\n    }\n\n    // Links.\n    self.link_pair(net, Pair::new(a, b));\n\n    true\n  }\n\n  // The Call Interaction.\n  pub fn interact_call(&mut self, net: &GNet, a: Port, b: Port, book: &Book) -> bool {\n    let fid = (a.get_val() as usize) & 0xFFFFFFF;\n    let def = &book.defs[fid];\n\n    // Copy Optimization.\n    if b.get_tag() == DUP {\n      if def.safe {\n        return self.interact_eras(net, a, b);\n      } else {\n        // TODO:\n        // Currently, we'll not allow copying of REFs with DUPs. While this is perfectly valid on\n        // IC semantics (i.e., if the user know what they're doing), this can lead to unsound\n        // reductions when compiling λ-terms to HVM. So, for now, we'll just disable this feature,\n        // and consider it undefined behavior. We should add a `--unsafe` flag that allows it.\n        println!(\"ERROR: attempt to clone a non-affine global reference.\\n\");\n        std::process::exit(0);\n      }\n    }\n\n    // Allocates needed nodes and vars.\n    if !self.get_resources(net, def.rbag.len() + 1, def.node.len(), def.vars as usize) {\n      return false;\n    }\n\n    // Stores new vars.\n    for i in 0..def.vars {\n      net.vars_create(self.vloc[i], NONE);\n      //println!(\"vars_create vars_loc[{:04X}] {:04X}\", i, self.vloc[i]);\n    }\n\n    // Stores new nodes.\n    for i in 0..def.node.len() {\n      net.node_create(self.nloc[i], def.node[i].adjust_pair(self));\n      //println!(\"node_create node_loc[{:04X}] {:016X}\", i-1, def.node[i].0);\n    }\n\n    // Links.\n    for pair in &def.rbag {\n      self.link_pair(net, pair.adjust_pair(self));\n    }\n    self.link_pair(net, Pair::new(def.root.adjust_port(self), b));\n\n    true\n  }\n\n  // The Void Interaction.\n  pub fn interact_void(&mut self, _net: &GNet, _a: Port, _b: Port) -> bool {\n    true\n  }\n\n  // The Eras Interaction.\n  pub fn interact_eras(&mut self, net: &GNet, a: Port, b: Port) -> bool {\n    // Allocates needed nodes and vars.\n    if !self.get_resources(net, 2, 0, 0) {\n      return false;\n    }\n\n    // Checks availability\n    if net.node_load(b.get_val() as usize).0 == 0 {\n      return false;\n    }\n\n    // Loads ports.\n    let b_ = net.node_exchange(b.get_val() as usize, Pair(0));\n    let b1 = b_.get_fst();\n    let b2 = b_.get_snd();\n\n    // Links.\n    self.link_pair(net, Pair::new(a, b1));\n    self.link_pair(net, Pair::new(a, b2));\n\n    true\n  }\n\n  // The Anni Interaction.\n  pub fn interact_anni(&mut self, net: &GNet, a: Port, b: Port) -> bool {\n    // Allocates needed nodes and vars.\n    if !self.get_resources(net, 2, 0, 0) {\n      return false;\n    }\n\n    // Checks availability\n    if net.node_load(a.get_val() as usize).0 == 0 || net.node_load(b.get_val() as usize).0 == 0 {\n      return false;\n    }\n\n    // Loads ports.\n    let a_ = net.node_take(a.get_val() as usize);\n    let a1 = a_.get_fst();\n    let a2 = a_.get_snd();\n    let b_ = net.node_take(b.get_val() as usize);\n    let b1 = b_.get_fst();\n    let b2 = b_.get_snd();\n\n    // Links.\n    self.link_pair(net, Pair::new(a1, b1));\n    self.link_pair(net, Pair::new(a2, b2));\n\n    return true;\n  }\n\n  // The Comm Interaction.\n  pub fn interact_comm(&mut self, net: &GNet, a: Port, b: Port) -> bool {\n    // Allocates needed nodes and vars.\n    if !self.get_resources(net, 4, 4, 4) {\n      return false;\n    }\n\n    // Checks availability\n    if net.node_load(a.get_val() as usize).0 == 0 || net.node_load(b.get_val() as usize).0 == 0 {\n      return false;\n    }\n\n    // Loads ports.\n    let a_ = net.node_take(a.get_val() as usize);\n    let a1 = a_.get_fst();\n    let a2 = a_.get_snd();\n    let b_ = net.node_take(b.get_val() as usize);\n    let b1 = b_.get_fst();\n    let b2 = b_.get_snd();\n\n    // Stores new vars.\n    net.vars_create(self.vloc[0], NONE);\n    net.vars_create(self.vloc[1], NONE);\n    net.vars_create(self.vloc[2], NONE);\n    net.vars_create(self.vloc[3], NONE);\n\n    // Stores new nodes.\n    net.node_create(self.nloc[0], Pair::new(Port::new(VAR, self.vloc[0] as u32), Port::new(VAR, self.vloc[1] as u32)));\n    net.node_create(self.nloc[1], Pair::new(Port::new(VAR, self.vloc[2] as u32), Port::new(VAR, self.vloc[3] as u32)));\n    net.node_create(self.nloc[2], Pair::new(Port::new(VAR, self.vloc[0] as u32), Port::new(VAR, self.vloc[2] as u32)));\n    net.node_create(self.nloc[3], Pair::new(Port::new(VAR, self.vloc[1] as u32), Port::new(VAR, self.vloc[3] as u32)));\n\n    // Links.\n    self.link_pair(net, Pair::new(Port::new(b.get_tag(), self.nloc[0] as u32), a1));\n    self.link_pair(net, Pair::new(Port::new(b.get_tag(), self.nloc[1] as u32), a2));\n    self.link_pair(net, Pair::new(Port::new(a.get_tag(), self.nloc[2] as u32), b1));\n    self.link_pair(net, Pair::new(Port::new(a.get_tag(), self.nloc[3] as u32), b2));\n\n    true\n  }\n\n  // The Oper Interaction.\n  pub fn interact_oper(&mut self, net: &GNet, a: Port, b: Port) -> bool {\n    // Allocates needed nodes and vars.\n    if !self.get_resources(net, 1, 1, 0) {\n      return false;\n    }\n\n    // Checks availability\n    if net.node_load(b.get_val() as usize).0 == 0 {\n      return false;\n    }\n\n    assert_eq!(a.get_tag(), NUM);\n    // Loads ports.\n    let av = a.get_val();\n    let b_ = net.node_take(b.get_val() as usize);\n    let b1 = b_.get_fst();\n    let b2 = net.enter(b_.get_snd());\n\n    // Performs operation.\n    if b1.get_tag() == NUM {\n      let bv = b1.get_val();\n      let cv = Numb::operate(Numb(av), Numb(bv));\n      self.link_pair(net, Pair::new(Port::new(NUM, cv.0), b2));\n    } else {\n      net.node_create(self.nloc[0], Pair::new(Port::new(a.get_tag(), Numb(a.get_val()).0), b2));\n      self.link_pair(net, Pair::new(b1, Port::new(OPR, self.nloc[0] as u32)));\n    }\n\n    true\n  }\n\n  // The Swit Interaction.\n  pub fn interact_swit(&mut self, net: &GNet, a: Port, b: Port) -> bool {\n    // Allocates needed nodes and vars.\n    if !self.get_resources(net, 1, 2, 0) {\n      return false;\n    }\n\n    // Checks availability\n    if net.node_load(b.get_val() as usize).0 == 0 {\n      return false;\n    }\n\n    // Loads ports.\n    let av = Numb(a.get_val()).get_u24();\n    let b_ = net.node_take(b.get_val() as usize);\n    let b1 = b_.get_fst();\n    let b2 = b_.get_snd();\n\n    // Stores new nodes.\n    if av == 0 {\n      net.node_create(self.nloc[0], Pair::new(b2, Port::new(ERA,0)));\n      self.link_pair(net, Pair::new(Port::new(CON, self.nloc[0] as u32), b1));\n    } else {\n      net.node_create(self.nloc[0], Pair::new(Port::new(ERA,0), Port::new(CON, self.nloc[1] as u32)));\n      net.node_create(self.nloc[1], Pair::new(Port::new(NUM, Numb::new_u24(av-1).0), b2));\n      self.link_pair(net, Pair::new(Port::new(CON, self.nloc[0] as u32), b1));\n    }\n\n    true\n  }\n\n  // Pops a local redex and performs a single interaction.\n  pub fn interact(&mut self, net: &GNet, book: &Book) -> bool {\n    // Pops a redex.\n    let redex = match self.rbag.pop_redex() {\n      Some(redex) => redex,\n      None => return true, // If there is no redex, stop\n    };\n\n    // Gets redex ports A and B.\n    let mut a = redex.get_fst();\n    let mut b = redex.get_snd();\n\n    // Gets the rule type.\n    let mut rule = Port::get_rule(a, b);\n\n    // Used for root redex.\n    if a.get_tag() == REF && b == ROOT {\n      rule = CALL;\n    // Swaps ports if necessary.\n    } else if Port::should_swap(a,b) {\n      let x = a; a = b; b = x;\n    }\n\n    //println!(\"[{:04x}] REDUCE {} ~ {} | {}\", self.tid, a.show(), b.show(), rule);\n\n    let success = match rule {\n      LINK => self.interact_link(net, a, b),\n      CALL => self.interact_call(net, a, b, book),\n      VOID => self.interact_void(net, a, b),\n      ERAS => self.interact_eras(net, a, b),\n      ANNI => self.interact_anni(net, a, b),\n      COMM => self.interact_comm(net, a, b),\n      OPER => self.interact_oper(net, a, b),\n      SWIT => self.interact_swit(net, a, b),\n      _    => panic!(\"Invalid rule\"),\n    };\n\n    // If error, pushes redex back.\n    if !success {\n      self.rbag.push_redex(redex);\n      false\n    // Else, increments the interaction count.\n    } else if rule != LINK {\n      self.itrs += 1;\n      true\n    } else {\n      true\n    }\n  }\n\n  pub fn evaluator(&mut self, net: &GNet, book: &Book) {\n    // Increments the tick\n    self.tick += 1;\n\n    // DEBUG:\n    //let mut max_rlen = 0;\n    //let mut max_nlen = 0;\n    //let mut max_vlen = 0;\n\n    // Performs some interactions\n    while self.rbag.len() > 0 {\n      self.interact(net, book);\n\n      // DEBUG:\n      //println!(\"{}{}\", self.rbag.show(), net.show());\n      //println!(\"\");\n      //let rlen = self.rbag.lo.len() + self.rbag.hi.len();\n      //let mut nlen = 0;\n      //for i in 0 .. 256 {\n        //if net.node_load(i).0 != 0 {\n          //nlen += 1;\n        //}\n      //}\n      //let mut vlen = 0;\n      //for i in 0..256 {\n        //if net.vars_load(i).0 != 0 {\n          //vlen += 1;\n        //}\n      //}\n      //max_rlen = max_rlen.max(rlen);\n      //max_nlen = max_nlen.max(nlen);\n      //max_vlen = max_vlen.max(vlen);\n\n    }\n\n    // DEBUG:\n    //println!(\"MAX_RLEN: {}\", max_rlen);\n    //println!(\"MAX_NLEN: {}\", max_nlen);\n    //println!(\"MAX_VLEN: {}\", max_vlen);\n\n    net.itrs.fetch_add(self.itrs as u64, Ordering::Relaxed);\n    self.itrs = 0;\n  }\n}\n\n// Serialization\n// -------------\n\nimpl Book {\n  pub fn to_buffer(&self, buf: &mut Vec<u8>) {\n    // Writes the number of defs\n    buf.extend_from_slice(&(self.defs.len() as u32).to_ne_bytes());\n\n    // For each def\n    for (fid, def) in self.defs.iter().enumerate() {\n      // Writes the safe flag\n      buf.extend_from_slice(&(fid as u32).to_ne_bytes());\n\n      // Writes the name\n      // TODO: store as varlen to save space\n      let name_bytes = def.name.as_bytes();\n      if name_bytes.len() < 256 {\n        buf.extend_from_slice(&name_bytes[..name_bytes.len()]);\n        buf.resize(buf.len() + (256 - name_bytes.len()), 0);\n      } else {\n        panic!(\"Name too long: {}\", def.name);\n      }\n\n      // Writes the safe flag\n      buf.extend_from_slice(&(def.safe as u32).to_ne_bytes());\n\n      // Writes the rbag length\n      buf.extend_from_slice(&(def.rbag.len() as u32).to_ne_bytes());\n\n      // Writes the node length\n      buf.extend_from_slice(&(def.node.len() as u32).to_ne_bytes());\n\n      // Writes the vars length\n      buf.extend_from_slice(&(def.vars as u32).to_ne_bytes());\n\n      // Writes the root\n      buf.extend_from_slice(&def.root.0.to_ne_bytes());\n\n      // Writes the rbag buffer\n      for pair in &def.rbag {\n        buf.extend_from_slice(&pair.0.to_ne_bytes());\n      }\n\n      // Writes the node buffer\n      for pair in &def.node {\n        buf.extend_from_slice(&pair.0.to_ne_bytes());\n      }\n    }\n  }\n}\n\n// Debug\n// -----\n\nimpl Port {\n  pub fn show(&self) -> String {\n    match self.get_tag() {\n      VAR => format!(\"VAR:{:08X}\", self.get_val()),\n      REF => format!(\"REF:{:08X}\", self.get_val()),\n      ERA => format!(\"ERA:{:08X}\", self.get_val()),\n      NUM => format!(\"NUM:{:08X}\", self.get_val()),\n      CON => format!(\"CON:{:08X}\", self.get_val()),\n      DUP => format!(\"DUP:{:08X}\", self.get_val()),\n      OPR => format!(\"OPR:{:08X}\", self.get_val()),\n      SWI => format!(\"SWI:{:08X}\", self.get_val()),\n      _   => panic!(\"Invalid tag\"),\n    }\n  }\n}\n\nimpl Pair {\n  pub fn show(&self) -> String {\n    format!(\"{} ~ {}\", self.get_fst().show(), self.get_snd().show())\n  }\n}\n\nimpl RBag {\n  pub fn show(&self) -> String {\n    let mut s = String::new();\n    s.push_str(\"RBAG | FST-TREE     | SND-TREE    \\n\");\n    s.push_str(\"---- | ------------ | ------------\\n\");\n    for (i, pair) in self.hi.iter().enumerate() {\n      s.push_str(&format!(\"{:04X} | {} | {}\\n\", i, pair.get_fst().show(), pair.get_snd().show()));\n    }\n    s.push_str(\"~~~~ | ~~~~~~~~~~~~ | ~~~~~~~~~~~~\\n\");\n    for (i, pair) in self.lo.iter().enumerate() {\n      s.push_str(&format!(\"{:04X} | {} | {}\\n\", i + self.hi.len(), pair.get_fst().show(), pair.get_snd().show()));\n    }\n    s.push_str(\"==== | ============ | ============\\n\");\n    return s;\n  }\n}\n\nimpl<'a> GNet<'a> {\n  pub fn show(&self) -> String {\n    let mut s = String::new();\n    s.push_str(\"NODE | FST-PORT     | SND-PORT     \\n\");\n    s.push_str(\"---- | ------------ | ------------\\n\");\n    //for i in 0..256 {\n    for i in 0..self.nlen-1 {\n      let node = self.node_load(i);\n      if node.0 != 0 {\n        s.push_str(&format!(\"{:04X} | {} | {}\\n\", i, node.get_fst().show(), node.get_snd().show()));\n      }\n    }\n    s.push_str(\"==== | ============ | ============\\n\");\n    s.push_str(\"VARS | VALUE        |\\n\");\n    s.push_str(\"---- | ------------ |\\n\");\n    //for i in 0..256 {\n    for i in 0..self.vlen-1 {\n      let var = self.vars_load(i);\n      if var.0 != 0 {\n        s.push_str(&format!(\"{:04X} | {} |\\n\", i, var.show()));\n      }\n    }\n    let root = self.vars_load(0x1FFFFFFF);\n    s.push_str(&format!(\"ROOT | {} |\\n\", root.show()));\n    s.push_str(\"==== | ============ |\\n\");\n    return s;\n  }\n}\n\nimpl Book {\n  pub fn show(&self) -> String {\n    let mut s = String::new();\n    for def in &self.defs {\n      s.push_str(&format!(\"==== | ============ | ============ {} (vars={},safe={})\\n\", def.name, def.vars, def.safe));\n      s.push_str(\"NODE | FST-PORT     | SND-PORT     \\n\");\n      s.push_str(\"---- | ------------ | ------------\\n\");\n      for (i, node) in def.node.iter().enumerate() {\n        s.push_str(&format!(\"{:04X} | {} | {}\\n\", i, node.get_fst().show(), node.get_snd().show()));\n      }\n      s.push_str(\"==== | ============ | ============\\n\");\n      s.push_str(\"RBAG | FST-TREE     | SND-TREE    \\n\");\n      s.push_str(\"---- | ------------ | ------------\\n\");\n      for (i, node) in def.rbag.iter().enumerate() {\n        s.push_str(&format!(\"{:04X} | {} | {}\\n\", i, node.get_fst().show(), node.get_snd().show()));\n      }\n      s.push_str(\"==== | ============ | ============\\n\");\n\n    }\n    return s;\n  }\n}\n\nimpl Book {\n  // Creates a demo program that is equivalent to:\n  //   lop  = λn switch n { 0: 0; _: (lop n-1) }\n  //   fun  = λn switch n { 0: (lop LOOPS); _: (+ (fun n-1) (fun n-1)) }\n  //   main = (fun DEPTH)\n  // Or, in core syntax:\n  //   @fun  = (?<(@fun0 @fun1) a> a)\n  //   @fun0 = a & @lop ~ (#65536 a)\n  //   @fun1 = ({a b} c) & @fun ~ (a <+ d c>) & @fun ~ (b d)\n  //   @lop  = (?<(#0 @lop0) a> a)\n  //   @lop0 = (a b) & @lop ~ (a b)\n  //   @main = a & @fun ~ (#10 a)\n  //pub fn new_demo(depth: u32, loops: u32) -> Self {\n    //let fun = Def {\n      //name: \"fun\".to_string(),\n      //safe: true,\n      //rbag: vec![],\n      //node: vec![Pair::new(Port(0x0C),Port(0x00)), Pair::new(Port(0x1F),Port(0x00)), Pair::new(Port(0x09),Port(0x11)), Pair::new(Port(0x14),Port(0x00))],\n      //vars: 1,\n    //};\n    //let fun0 = Def {\n      //name: \"fun0\".to_string(),\n      //safe: true,\n      //rbag: vec![Pair::new(Port(0x19),Port(0x0C))],\n      //node: vec![Pair::new(Port(0x00),Port(0x00)), Pair::new(Port::new(NUM,loops),Port(0x00))],\n      //vars: 1,\n    //};\n    //let fun1 = Def {\n      //name: \"fun1\".to_string(),\n      //safe: false,\n      //rbag: vec![Pair::new(Port(0x01),Port(0x1C)), Pair::new(Port(0x01),Port(0x2C))],\n      //node: vec![Pair::new(Port(0x0C),Port(0x00)), Pair::new(Port(0x15),Port(0x10)), Pair::new(Port(0x00),Port(0x08)), Pair::new(Port(0x00),Port(0x26)), Pair::new(Port(0x18),Port(0x10)), Pair::new(Port(0x08),Port(0x18))],\n      //vars: 4,\n    //};\n    //let lop = Def {\n      //name: \"lop\".to_string(),\n      //safe: true,\n      //rbag: vec![],\n      //node: vec![Pair::new(Port(0x0C),Port(0x00)), Pair::new(Port(0x1F),Port(0x00)), Pair::new(Port(0x03),Port(0x21)), Pair::new(Port(0x14),Port(0x00))],\n      //vars: 1,\n    //};\n    //let lop0 = Def {\n      //name: \"lop0\".to_string(),\n      //safe: true,\n      //rbag: vec![Pair::new(Port(0x19),Port(0x14))],\n      //node: vec![Pair::new(Port(0x0C),Port(0x00)), Pair::new(Port(0x00),Port(0x08)), Pair::new(Port(0x00),Port(0x08))],\n      //vars: 2,\n    //};\n    //let main = Def {\n      //name: \"main\".to_string(),\n      //safe: true,\n      //rbag: vec![Pair::new(Port(0x01),Port(0x0C))],\n      //node: vec![Pair::new(Port(0x00),Port(0x00)), Pair::new(Port::new(NUM,depth),Port(0x00))],\n      //vars: 1,\n    //};\n    //return Book {\n      //defs: vec![fun, fun0, fun1, lop, lop0, main],\n    //};\n  //}\n}\n\n#[test]\nfn test_f24() {\n  // Test that numbers in range round-trip correctly:\n  let min_positive  = f32::from_bits(0b0_00000000_000000000000001_00000000);\n  let max_subnormal = f32::from_bits(0b0_00000000_111111111111111_00000000);\n  let min_normal    = f32::from_bits(0b0_00000001_000000000000000_00000000);\n  for x in [\n    0.0,\n    -0.0,\n    1.0,\n    -1.0,\n    1.1000061,\n    -1.1000061,\n    f32::NAN,\n    f32::NEG_INFINITY,\n    f32::INFINITY,\n    min_positive,\n    -min_positive,\n    min_positive * 123.0,\n    -min_positive * 123.0,\n    max_subnormal,\n    min_normal,\n  ] {\n    let y = Numb::new_f24(x).get_f24();\n    assert!(x.is_nan() && y.is_nan() || x == y);\n  }\n\n  for (i, o) in [\n    // Test rounding ties to even:\n    (f32::from_bits(0b00_00000000), f32::from_bits(0b00_00000000)),\n    (f32::from_bits(0b00_01111111), f32::from_bits(0b00_00000000)),\n    (f32::from_bits(0b00_10000000), f32::from_bits(0b00_00000000)),\n    (f32::from_bits(0b00_10000001), f32::from_bits(0b01_00000000)),\n    (f32::from_bits(0b00_11111111), f32::from_bits(0b01_00000000)),\n    (f32::from_bits(0b01_00000000), f32::from_bits(0b01_00000000)),\n    (f32::from_bits(0b01_01111111), f32::from_bits(0b01_00000000)),\n    (f32::from_bits(0b01_10000000), f32::from_bits(0b10_00000000)),\n    (f32::from_bits(0b01_10000001), f32::from_bits(0b10_00000000)),\n    (f32::from_bits(0b01_11111111), f32::from_bits(0b10_00000000)),\n  ] {\n    assert_eq!(Numb::new_f24(i).get_f24(), o);\n  }\n\n  // Test that NaNs are not turned into infinities\n  assert!(Numb::new_f24(f32::from_bits(0b0_11111111_000000000000000_00000001)).get_f24().is_nan());\n  assert!(Numb::new_f24(f32::from_bits(0b1_11111111_000000000000000_00000001)).get_f24().is_nan());\n  assert!(Numb::new_f24(f32::from_bits(0b0_11111111_111111111111111_11111111)).get_f24().is_nan());\n}\n"
  },
  {
    "path": "src/lib.rs",
    "content": "#![allow(dead_code)]\n#![allow(unused_imports)]\n#![allow(unused_variables)]\n\npub mod ast;\npub mod cmp;\npub mod hvm;\n"
  },
  {
    "path": "src/main.rs",
    "content": "#![allow(dead_code)]\n#![allow(unused_imports)]\n#![allow(unused_variables)]\n\nuse clap::{Arg, ArgAction, Command};\nuse ::hvm::{ast, cmp, hvm};\nuse std::fs;\nuse std::io::Write;\nuse std::path::PathBuf;\nuse std::process::Command as SysCommand;\n\n#[cfg(feature = \"c\")]\nextern \"C\" {\n  fn hvm_c(book_buffer: *const u32);\n}\n\n#[cfg(feature = \"cuda\")]\nextern \"C\" {\n  fn hvm_cu(book_buffer: *const u32);\n}\n\nfn main() {\n  let matches = Command::new(\"hvm\")\n    .about(\"HVM2: Higher-order Virtual Machine 2 (32-bit Version)\")\n    .version(env!(\"CARGO_PKG_VERSION\"))\n    .subcommand_required(true)\n    .arg_required_else_help(true)\n    .subcommand(\n      Command::new(\"run\")\n        .about(\"Interprets a file (using Rust)\")\n        .arg(Arg::new(\"file\").required(true)))\n    .subcommand(\n      Command::new(\"run-c\")\n        .about(\"Interprets a file (using C)\")\n        .arg(Arg::new(\"file\").required(true))\n        .arg(Arg::new(\"io\")\n          .long(\"io\")\n          .action(ArgAction::SetTrue)\n          .help(\"Run with IO enabled\"))\n    )\n    .subcommand(\n      Command::new(\"run-cu\")\n        .about(\"Interprets a file (using CUDA)\")\n        .arg(Arg::new(\"file\").required(true))\n        .arg(Arg::new(\"io\")\n          .long(\"io\")\n          .action(ArgAction::SetTrue)\n          .help(\"Run with IO enabled\")))\n    .subcommand(\n      Command::new(\"gen-c\")\n        .about(\"Compiles a file with IO (to standalone C)\")\n        .arg(Arg::new(\"file\").required(true))\n        .arg(Arg::new(\"io\")\n          .long(\"io\")\n          .action(ArgAction::SetTrue)\n          .help(\"Generate with IO enabled\")))\n    .subcommand(\n      Command::new(\"gen-cu\")\n        .about(\"Compiles a file (to standalone CUDA)\")\n        .arg(Arg::new(\"file\").required(true))\n        .arg(Arg::new(\"io\")\n          .long(\"io\")\n          .action(ArgAction::SetTrue)\n          .help(\"Generate with IO enabled\")))\n    .get_matches();\n\n  match matches.subcommand() {\n    Some((\"run\", sub_matches)) => {\n      let file = sub_matches.get_one::<String>(\"file\").expect(\"required\");\n      let code = fs::read_to_string(file).expect(\"Unable to read file\");\n      let book = ast::Book::parse(&code).unwrap_or_else(|er| panic!(\"{}\",er)).build();\n      run(&book);\n    }\n    Some((\"run-c\", sub_matches)) => {\n      let file = sub_matches.get_one::<String>(\"file\").expect(\"required\");\n      let code = fs::read_to_string(file).expect(\"Unable to read file\");\n      let book = ast::Book::parse(&code).unwrap_or_else(|er| panic!(\"{}\",er)).build();\n      let mut data : Vec<u8> = Vec::new();\n      book.to_buffer(&mut data);\n      #[cfg(feature = \"c\")]\n      unsafe {\n        hvm_c(data.as_mut_ptr() as *mut u32);\n      }\n      #[cfg(not(feature = \"c\"))]\n      println!(\"C runtime not available!\\n\");\n    }\n    Some((\"run-cu\", sub_matches)) => {\n      let file = sub_matches.get_one::<String>(\"file\").expect(\"required\");\n      let code = fs::read_to_string(file).expect(\"Unable to read file\");\n      let book = ast::Book::parse(&code).unwrap_or_else(|er| panic!(\"{}\",er)).build();\n      let mut data : Vec<u8> = Vec::new();\n      book.to_buffer(&mut data);\n      #[cfg(feature = \"cuda\")]\n      unsafe {\n        hvm_cu(data.as_mut_ptr() as *mut u32);\n      }\n      #[cfg(not(feature = \"cuda\"))]\n      println!(\"CUDA runtime not available!\\n If you've installed CUDA and nvcc after HVM, please reinstall HVM.\");\n    }\n    Some((\"gen-c\", sub_matches)) => {\n      // Reads book from file\n      let file = sub_matches.get_one::<String>(\"file\").expect(\"required\");\n      let code = fs::read_to_string(file).expect(\"Unable to read file\");\n      let book = ast::Book::parse(&code).unwrap_or_else(|er| panic!(\"{}\",er)).build();\n\n      // Gets optimal core count\n      let cores = num_cpus::get();\n      let tpcl2 = (cores as f64).log2().floor() as u32;\n\n      // Generates the interpreted book\n      let mut book_buf : Vec<u8> = Vec::new();\n      book.to_buffer(&mut book_buf);\n      let bookb = format!(\"{:?}\", book_buf).replace(\"[\",\"{\").replace(\"]\",\"}\");\n      let bookb = format!(\"static const u8 BOOK_BUF[] = {};\", bookb);\n\n      // Generates the C file\n      let hvm_c = include_str!(\"hvm.c\");\n      let hvm_c = format!(\"#define IO\\n\\n{hvm_c}\");\n      let hvm_c = hvm_c.replace(\"///COMPILED_INTERACT_CALL///\", &cmp::compile_book(cmp::Target::C, &book));\n      let hvm_c = hvm_c.replace(\"#define INTERPRETED\", \"#define COMPILED\");\n      let hvm_c = hvm_c.replace(\"//COMPILED_BOOK_BUF//\", &bookb);\n      let hvm_c = hvm_c.replace(\"#define WITHOUT_MAIN\", \"#define WITH_MAIN\");\n      let hvm_c = hvm_c.replace(\"#define TPC_L2 0\", &format!(\"#define TPC_L2 {} // {} cores\", tpcl2, cores));\n      let hvm_c = format!(\"{hvm_c}\\n\\n{}\", include_str!(\"run.c\"));\n      let hvm_c = hvm_c.replace(r#\"#include \"hvm.c\"\"#, \"\");\n      println!(\"{}\", hvm_c);\n    }\n    Some((\"gen-cu\", sub_matches)) => {\n      // Reads book from file\n      let file = sub_matches.get_one::<String>(\"file\").expect(\"required\");\n      let code = fs::read_to_string(file).expect(\"Unable to read file\");\n      let book = ast::Book::parse(&code).unwrap_or_else(|er| panic!(\"{}\",er)).build();\n\n      // Generates the interpreted book\n      let mut book_buf : Vec<u8> = Vec::new();\n      book.to_buffer(&mut book_buf);\n      let bookb = format!(\"{:?}\", book_buf).replace(\"[\",\"{\").replace(\"]\",\"}\");\n      let bookb = format!(\"static const u8 BOOK_BUF[] = {};\", bookb);\n\n      //FIXME: currently, CUDA is faster on interpreted mode, so the compiler uses it.\n\n      // Compile with compiled functions:\n      //let hvm_c = include_str!(\"hvm.cu\");\n      //let hvm_c = hvm_c.replace(\"///COMPILED_INTERACT_CALL///\", &cmp::compile_book(cmp::Target::CUDA, &book));\n      //let hvm_c = hvm_c.replace(\"#define INTERPRETED\", \"#define COMPILED\");\n      \n      // Generates the Cuda file\n      let hvm_cu = include_str!(\"hvm.cu\");\n      let hvm_cu = format!(\"#define IO\\n\\n{hvm_cu}\");\n      let hvm_cu = hvm_cu.replace(\"//COMPILED_BOOK_BUF//\", &bookb);\n      let hvm_cu = hvm_cu.replace(\"#define WITHOUT_MAIN\", \"#define WITH_MAIN\");\n      let hvm_cu = format!(\"{hvm_cu}\\n\\n{}\", include_str!(\"run.cu\"));\n      let hvm_cu = hvm_cu.replace(r#\"#include \"hvm.cu\"\"#, \"\");\n      println!(\"{}\", hvm_cu);\n    }\n    _ => unreachable!(),\n  }\n}\n\npub fn run(book: &hvm::Book) {\n  // Initializes the global net\n  let net = hvm::GNet::new(1 << 29, 1 << 29);\n\n  // Initializes threads\n  let mut tm = hvm::TMem::new(0, 1);\n\n  // Creates an initial redex that calls main\n  let main_id = book.defs.iter().position(|def| def.name == \"main\").unwrap();\n  tm.rbag.push_redex(hvm::Pair::new(hvm::Port::new(hvm::REF, main_id as u32), hvm::ROOT));\n  net.vars_create(hvm::ROOT.get_val() as usize, hvm::NONE);\n\n  // Starts the timer\n  let start = std::time::Instant::now();\n\n  // Evaluates\n  tm.evaluator(&net, &book);\n  \n  // Stops the timer\n  let duration = start.elapsed();\n\n  //println!(\"{}\", net.show());\n\n  // Prints the result\n  if let Some(tree) = ast::Net::readback(&net, book) {\n    println!(\"Result: {}\", tree.show());\n  } else {\n    println!(\"Readback failed. Printing GNet memdump...\\n\");\n    println!(\"{}\", net.show());\n  }\n\n  // Prints interactions and time\n  let itrs = net.itrs.load(std::sync::atomic::Ordering::Relaxed);\n  println!(\"- ITRS: {}\", itrs);\n  println!(\"- TIME: {:.2}s\", duration.as_secs_f64());\n  println!(\"- MIPS: {:.2}\", itrs as f64 / duration.as_secs_f64() / 1_000_000.0);\n}\n"
  },
  {
    "path": "src/run.c",
    "content": "#include <dlfcn.h>\n#include <errno.h>\n#include <stdio.h>\n#include \"hvm.c\"\n\n// Readback: λ-Encoded Ctr\ntypedef struct Ctr {\n  u32  tag;\n  u32  args_len;\n  Port args_buf[16];\n} Ctr;\n\n// Readback: Tuples\ntypedef struct Tup {\n  u32  elem_len;\n  Port elem_buf[8];\n} Tup;\n\n// Readback: λ-Encoded Str (UTF-32), null-terminated\n// FIXME: this is actually ASCII :|\ntypedef struct Str {\n  u32  len;\n  char *buf;\n} Str;\n\n// Readback: λ-Encoded list of bytes\ntypedef struct Bytes {\n  u32  len;\n  char *buf;\n} Bytes;\n\n// IO Magic Number\n#define IO_MAGIC_0 0xD0CA11\n#define IO_MAGIC_1 0xFF1FF1\n\n// IO Tags\n#define IO_DONE 0\n#define IO_CALL 1\n\n// Result Tags = Result<T, E>\n#define RESULT_OK  0\n#define RESULT_ERR 1\n\n// IOError = {\n//   Type,           -- a type error\n//   Name,           -- invalid io func name\n//   Inner {val: T}, -- an error while calling an io func\n// }\n#define IO_ERR_TYPE 0\n#define IO_ERR_NAME 1\n#define IO_ERR_INNER 2\n\ntypedef struct IOError {\n  u32 tag;\n  Port val;\n} IOError;\n\n// List Tags\n#define LIST_NIL  0\n#define LIST_CONS 1\n\n// Readback\n// --------\n\n// Reads back a λ-Encoded constructor from device to host.\n// Encoding: λt ((((t TAG) arg0) arg1) ...)\nCtr readback_ctr(Net* net, Book* book, Port port) {\n  Ctr ctr;\n  ctr.tag = -1;\n  ctr.args_len = 0;\n\n  // Loads root lambda\n  Port lam_port = expand(net, book, port);\n  if (get_tag(lam_port) != CON) return ctr;\n  Pair lam_node = node_load(net, get_val(lam_port));\n\n  // Loads first application\n  Port app_port = expand(net, book, get_fst(lam_node));\n  if (get_tag(app_port) != CON) return ctr;\n  Pair app_node = node_load(net, get_val(app_port));\n\n  // Loads first argument (as the tag)\n  Port arg_port = expand(net, book, get_fst(app_node));\n  if (get_tag(arg_port) != NUM) return ctr;\n  ctr.tag = get_u24(get_val(arg_port));\n\n  // Loads remaining arguments\n  while (true) {\n    app_port = expand(net, book, get_snd(app_node));\n    if (get_tag(app_port) != CON) break;\n    app_node = node_load(net, get_val(app_port));\n    arg_port = expand(net, book, get_fst(app_node));\n    ctr.args_buf[ctr.args_len++] = arg_port;\n  }\n\n  return ctr;\n}\n\n// Reads back a tuple of at most `size` elements. Tuples are\n// (right-nested con nodes) (CON 1 (CON 2 (CON 3 (...))))\n// The provided `port` should be `expanded` before calling.\nextern Tup readback_tup(Net* net, Book* book, Port port, u32 size) {\n  Tup tup;\n  tup.elem_len = 0;\n\n  // Loads remaining arguments\n  while (get_tag(port) == CON && (tup.elem_len + 1 < size)) {\n    Pair node = node_load(net, get_val(port));\n    tup.elem_buf[tup.elem_len++] = expand(net, book, get_fst(node));\n\n    port = expand(net, book, get_snd(node));\n  }\n\n  tup.elem_buf[tup.elem_len++] = port;\n\n  return tup;\n}\n\n// Converts a Port into a list of bytes.\n// Encoding:\n// - λt (t NIL)\n// - λt (((t CONS) head) tail)\nBytes readback_bytes(Net* net, Book* book, Port port) {\n  Bytes bytes;\n  u32 capacity = 256;\n  bytes.buf = (char*) malloc(sizeof(char) * capacity);\n  bytes.len = 0;\n\n  // Readback loop\n  while (true) {\n    // Normalizes the net\n    normalize(net, book);\n\n    // Reads the λ-Encoded Ctr\n    Ctr ctr = readback_ctr(net, book, peek(net, port));\n\n    // Reads string layer\n    switch (ctr.tag) {\n      case LIST_NIL: {\n        break;\n      }\n      case LIST_CONS: {\n        if (ctr.args_len != 2) break;\n        if (get_tag(ctr.args_buf[0]) != NUM) break;\n\n        if (bytes.len == capacity - 1) {\n          capacity *= 2;\n          bytes.buf = realloc(bytes.buf, capacity);\n        }\n\n        bytes.buf[bytes.len++] = get_u24(get_val(ctr.args_buf[0]));\n        boot_redex(net, new_pair(ctr.args_buf[1], ROOT));\n        port = ROOT;\n        continue;\n      }\n    }\n    break;\n  }\n\n  return bytes;\n}\n\n// Converts a Port into a UTF-32 (truncated to 24 bits) null-terminated string.\n// Since unicode scalars can fit in 21 bits, HVM's u24\n// integers can contain any unicode scalar value.\n// Encoding:\n// - λt (t NIL)\n// - λt (((t CONS) head) tail)\nStr readback_str(Net* net, Book* book, Port port) {\n  // readback_bytes is guaranteed to return a buffer with a capacity of at least one more\n  // than the number of bytes read, so we can null-terminate it.\n  Bytes bytes = readback_bytes(net, book, port);\n\n  Str str;\n  str.len = bytes.len;\n  str.buf = bytes.buf;\n  str.buf[str.len] = 0;\n\n  return str;\n}\n\n/// Returns a λ-Encoded Ctr for a NIL: λt (t NIL)\n/// A previous call to `get_resources(tm, 0, 2, 1)` is required.\nPort inject_nil(Net* net) {\n  u32 v1 = tm[0]->vloc[0];\n\n  u32 n1 = tm[0]->nloc[0];\n  u32 n2 = tm[0]->nloc[1];\n\n  vars_create(net, v1, NONE);\n  Port var = new_port(VAR, v1);\n\n  node_create(net, n1, new_pair(new_port(NUM, new_u24(LIST_NIL)), var));\n  node_create(net, n2, new_pair(new_port(CON, n1), var));\n\n  return new_port(CON, n2);\n}\n\n/// Returns a λ-Encoded Ctr for a CONS: λt (((t CONS) head) tail)\n/// A previous call to `get_resources(tm, 0, 4, 1)` is required.\nPort inject_cons(Net* net, Port head, Port tail) {\n  u32 v1 = tm[0]->vloc[0];\n\n  u32 n1 = tm[0]->nloc[0];\n  u32 n2 = tm[0]->nloc[1];\n  u32 n3 = tm[0]->nloc[2];\n  u32 n4 = tm[0]->nloc[3];\n\n  vars_create(net, v1, NONE);\n  Port var = new_port(VAR, v1);\n\n  node_create(net, n1, new_pair(tail, var));\n  node_create(net, n2, new_pair(head, new_port(CON, n1)));\n  node_create(net, n3, new_pair(new_port(NUM, new_u24(LIST_CONS)), new_port(CON, n2)));\n  node_create(net, n4, new_pair(new_port(CON, n3), var));\n\n  return new_port(CON, n4);\n}\n\n// Converts a list of bytes to a Port.\n// Encoding:\n// - λt (t NIL)\n// - λt (((t CONS) head) tail)\nPort inject_bytes(Net* net, Bytes *bytes) {\n  // Allocate all resources up front:\n  // - NIL needs  2 nodes & 1 var\n  // - CONS needs 4 nodes & 1 var\n  u32 len = bytes->len;\n  if (!get_resources(net, tm[0], 0, 2, 1)) {\n    fprintf(stderr, \"inject_bytes: failed to get resources\\n\");\n    return new_port(ERA, 0);\n  }\n  Port port = inject_nil(net);\n\n  // TODO: batch-allocate these (within the limits of TM)\n  for (u32 i = 0; i < len; i++) {\n    if (!get_resources(net, tm[0], 0, 4, 1)) {\n      fprintf(stderr, \"inject_bytes: failed to get resources\\n\");\n      return new_port(ERA, 0);\n    }\n    Port byte = new_port(NUM, new_u24(bytes->buf[len - i - 1]));\n    port = inject_cons(net, byte, port);\n  }\n\n  return port;\n}\n\n/// Returns a λ-Encoded Ctr for a RESULT_OK: λt ((t RESULT_OK) val)\nPort inject_ok(Net* net, Port val) {\n  if (!get_resources(net, tm[0], 0, 3, 1)) {\n    fprintf(stderr, \"inject_ok: failed to get resources\\n\");\n    return new_port(ERA, 0);\n  }\n\n  u32 v1 = tm[0]->vloc[0];\n\n  u32 n1 = tm[0]->nloc[0];\n  u32 n2 = tm[0]->nloc[1];\n  u32 n3 = tm[0]->nloc[2];\n\n  vars_create(net, v1, NONE);\n  Port var = new_port(VAR, v1);\n\n  node_create(net, n1, new_pair(val, var));\n  node_create(net, n2, new_pair(new_port(NUM, new_u24(RESULT_OK)), new_port(CON, n1)));\n  node_create(net, n3, new_pair(new_port(CON, n2), var));\n\n  return new_port(CON, n3);\n}\n\n/// Returns a λ-Encoded Ctr for a RESULT_ERR: λt ((t RESULT_ERR) err)\nPort inject_err(Net* net, Port err) {\n  if (!get_resources(net, tm[0], 0, 3, 1)) {\n    fprintf(stderr, \"inject_err: failed to get resources\\n\");\n    return new_port(ERA, 0);\n  }\n\n  u32 v1 = tm[0]->vloc[0];\n\n  u32 n1 = tm[0]->nloc[0];\n  u32 n2 = tm[0]->nloc[1];\n  u32 n3 = tm[0]->nloc[2];\n\n  vars_create(net, v1, NONE);\n  Port var = new_port(VAR, v1);\n\n  node_create(net, n1, new_pair(err, var));\n  node_create(net, n2, new_pair(new_port(NUM, new_u24(RESULT_ERR)), new_port(CON, n1)));\n  node_create(net, n3, new_pair(new_port(CON, n2), var));\n\n  return new_port(CON, n3);\n}\n\n/// Returns a λ-Encoded Ctr for a Result/Err(IOError(..))\nPort inject_io_err(Net* net, IOError err) {\n  if (err.tag <= IO_ERR_NAME) {\n    if (!get_resources(net, tm[0], 0, 2, 1)) {\n      fprintf(stderr, \"inject_io_err: failed to get resources\\n\");\n      return new_port(ERA, 0);\n    }\n\n    u32 v1 = tm[0]->vloc[0];\n\n    u32 n1 = tm[0]->nloc[0];\n    u32 n2 = tm[0]->nloc[1];\n\n    vars_create(net, v1, NONE);\n    Port var = new_port(VAR, v1);\n\n    node_create(net, n1, new_pair(new_port(NUM, new_u24(err.tag)), var));\n    node_create(net, n2, new_pair(new_port(CON, n1), var));\n\n    return inject_err(net, new_port(CON, n2));\n  }\n\n  if (!get_resources(net, tm[0], 0, 3, 1)) {\n    fprintf(stderr, \"inject_io_err: failed to get resources\\n\");\n    return new_port(ERA, 0);\n  }\n\n  u32 v1 = tm[0]->vloc[0];\n\n  u32 n1 = tm[0]->nloc[0];\n  u32 n2 = tm[0]->nloc[1];\n  u32 n3 = tm[0]->nloc[2];\n\n  vars_create(net, v1, NONE);\n  Port var = new_port(VAR, v1);\n\n  node_create(net, n1, new_pair(err.val, var));\n  node_create(net, n2, new_pair(new_port(NUM, new_u24(IO_ERR_INNER)), new_port(CON, n1)));\n  node_create(net, n3, new_pair(new_port(CON, n2), var));\n\n  return inject_err(net, new_port(CON, n3));\n}\n\n/// Returns a λ-Encoded Ctr for a Result/Err(IOError/Type)\nPort inject_io_err_type(Net* net) {\n  IOError io_error = {\n    .tag = IO_ERR_TYPE,\n  };\n\n  return inject_io_err(net, io_error);\n}\n\n/// Returns a λ-Encoded Ctr for a Result/Err(IOError/Name)\nPort inject_io_err_name(Net* net) {\n  IOError io_error = {\n    .tag = IO_ERR_NAME,\n  };\n\n  return inject_io_err(net, io_error);\n}\n\n/// Returns a λ-Encoded Ctr for a Result/Err(IOError/Inner(val))\nPort inject_io_err_inner(Net* net, Port val) {\n  IOError io_error = {\n    .tag = IO_ERR_INNER,\n    .val = val,\n  };\n\n  return inject_io_err(net, io_error);\n}\n\n/// Returns a λ-Encoded Ctr for an Result<T, IOError<String>>\n/// `err` must be `NUL`-terminated.\nPort inject_io_err_str(Net* net, char* err) {\n  Bytes err_bytes;\n  err_bytes.buf = err;\n  err_bytes.len = strlen(err_bytes.buf);\n  Port err_port = inject_bytes(net, &err_bytes);\n\n  return inject_io_err_inner(net, err_port);\n}\n\n// Primitive IO Fns\n// -----------------\n\n// Open file pointers. Indices into this array\n// are used as \"file descriptors\".\n// Indices 0 1 and 2 are reserved.\n// - 0 -> stdin\n// - 1 -> stdout\n// - 2 -> stderr\nstatic FILE* FILE_POINTERS[256];\n\n// Open dylibs handles. Indices into this array\n// are used as opaque loadedd object \"handles\".\nstatic void* DYLIBS[256];\n\n// Converts a NUM port (file descriptor) to file pointer.\nFILE* readback_file(Port port) {\n  if (get_tag(port) != NUM) {\n    fprintf(stderr, \"non-num where file descriptor was expected: %i\\n\", get_tag(port));\n    return NULL;\n  }\n\n  u32 idx = get_u24(get_val(port));\n\n  if (idx == 0) return stdin;\n  if (idx == 1) return stdout;\n  if (idx == 2) return stderr;\n\n  FILE* fp = FILE_POINTERS[idx];\n  if (fp == NULL) {\n    return NULL;\n  }\n\n  return fp;\n}\n\n// Converts a NUM port (dylib handle) to an opaque dylib object.\nvoid* readback_dylib(Port port) {\n  if (get_tag(port) != NUM) {\n    fprintf(stderr, \"non-num where dylib handle was expected: %i\\n\", get_tag(port));\n    return NULL;\n  }\n\n  u32 idx = get_u24(get_val(port));\n\n  void* dl = DYLIBS[idx];\n  if (dl == NULL) {\n    fprintf(stderr, \"invalid dylib handle\\n\");\n    return NULL;\n  }\n\n  return dl;\n}\n\n// Reads from a file a specified number of bytes.\n// `argm` is a tuple of (file_descriptor, num_bytes).\n// Returns: Result<Bytes, IOError<i24>>\nPort io_read(Net* net, Book* book, Port argm) {\n  Tup tup = readback_tup(net, book, argm, 2);\n  if (tup.elem_len != 2) {\n    return inject_io_err_type(net);\n  }\n\n  FILE* fp = readback_file(tup.elem_buf[0]);\n  u32 num_bytes = get_u24(get_val(tup.elem_buf[1]));\n\n  if (fp == NULL) {\n    return inject_io_err_inner(net, new_port(NUM, new_i24(EBADF)));\n  }\n\n  /// Read a string.\n  Bytes bytes;\n  bytes.buf = (char*) malloc(sizeof(char) * num_bytes);\n  bytes.len = fread(bytes.buf, sizeof(char), num_bytes, fp);\n\n  if ((bytes.len != num_bytes) && ferror(fp)) {\n    free(bytes.buf);\n    return inject_io_err_inner(net, new_port(NUM, new_i24(ferror(fp))));\n  }\n\n  // Convert it to a port.\n  Port ret = inject_bytes(net, &bytes);\n  free(bytes.buf);\n\n  return inject_ok(net, ret);\n}\n\n// Opens a file with the provided mode.\n// `argm` is a tuple (CON node) of the\n// file name and mode as strings.\n// Returns: Result<File, IOError<i24>>\nPort io_open(Net* net, Book* book, Port argm) {\n  Tup tup = readback_tup(net, book, argm, 2);\n  if (tup.elem_len != 2) {\n    return inject_io_err_type(net);\n  }\n\n  Str name = readback_str(net, book, tup.elem_buf[0]);\n  Str mode = readback_str(net, book, tup.elem_buf[1]);\n\n  for (u32 fd = 3; fd < sizeof(FILE_POINTERS); fd++) {\n    if (FILE_POINTERS[fd] == NULL) {\n      FILE_POINTERS[fd] = fopen(name.buf, mode.buf);\n\n      free(name.buf);\n      free(mode.buf);\n\n      if (FILE_POINTERS[fd] == NULL) {\n        return inject_io_err_inner(net, new_port(NUM, new_i24(errno)));\n      }\n\n      return inject_ok(net, new_port(NUM, new_u24(fd)));\n    }\n  }\n\n  free(name.buf);\n  free(mode.buf);\n\n  // too many open files\n  return inject_io_err_inner(net, new_port(NUM, new_i24(EMFILE)));\n}\n\n// Closes a file, reclaiming the file descriptor.\n// Returns: Result<*, IOError<i24>>\nPort io_close(Net* net, Book* book, Port argm) {\n  FILE* fp = readback_file(argm);\n  if (fp == NULL) {\n    return inject_io_err_inner(net, new_port(NUM, new_i24(EBADF)));\n  }\n\n  if (fclose(fp) != 0) {\n    return inject_io_err_inner(net, new_port(NUM, new_i24(ferror(fp))));\n  }\n\n  FILE_POINTERS[get_u24(get_val(argm))] = NULL;\n\n  return inject_ok(net, new_port(ERA, 0));\n}\n\n// Writes a list of bytes to a file.\n// `argm` is a tuple (CON node) of the\n// file descriptor and list of bytes to write.\n// Returns: Result<*, IOError<i24>>\nPort io_write(Net* net, Book* book, Port argm) {\n  Tup tup = readback_tup(net, book, argm, 2);\n  if (tup.elem_len != 2) {\n    return inject_io_err_type(net);\n  }\n\n  FILE* fp = readback_file(tup.elem_buf[0]);\n  Bytes bytes = readback_bytes(net, book, tup.elem_buf[1]);\n\n  if (fp == NULL) {\n    free(bytes.buf);\n\n    return inject_io_err_inner(net, new_port(NUM, new_i24(EBADF)));\n  }\n\n  if (fwrite(bytes.buf, sizeof(char), bytes.len, fp) != bytes.len) {\n    free(bytes.buf);\n\n    return inject_io_err_inner(net, new_port(NUM, new_i24(ferror(fp))));\n  }\n\n  free(bytes.buf);\n\n  return inject_ok(net, new_port(ERA, 0));\n}\n\n// Flushes an output stream.\n// Returns: Result<*, IOError<i24>>\nPort io_flush(Net* net, Book* book, Port argm) {\n  FILE* fp = readback_file(argm);\n  if (fp == NULL) {\n    return inject_io_err_inner(net, new_port(NUM, new_i24(EBADF)));\n  }\n\n  if (fflush(fp) != 0) {\n    return inject_io_err_inner(net, new_port(NUM, new_i24(ferror(fp))));\n  }\n\n  return inject_ok(net, new_port(ERA, 0));\n}\n\n// Seeks to a position in a file.\n// `argm` is a 3-tuple (CON fd (CON offset whence)), where\n// - fd is a file descriptor\n// - offset is a signed byte offset\n// - whence is what that offset is relative to:\n//    - 0 (SEEK_SET): beginning of file\n//    - 1 (SEEK_CUR): current position of the file pointer\n//    - 2 (SEEK_END): end of the file\n// Returns: Result<*, IOError<i24>>\nPort io_seek(Net* net, Book* book, Port argm) {\n  Tup tup = readback_tup(net, book, argm, 3);\n  if (tup.elem_len != 3) {\n    return inject_io_err_type(net);\n  }\n\n  FILE* fp = readback_file(tup.elem_buf[0]);\n  i32 offset = get_i24(get_val(tup.elem_buf[1]));\n  u32 whence = get_i24(get_val(tup.elem_buf[2]));\n\n  if (fp == NULL) {\n    return inject_io_err_inner(net, new_port(NUM, new_i24(EBADF)));\n  }\n\n  int cwhence;\n  switch (whence) {\n    case 0: cwhence = SEEK_SET; break;\n    case 1: cwhence = SEEK_CUR; break;\n    case 2: cwhence = SEEK_END; break;\n    default:\n      return inject_io_err_type(net);\n  }\n\n  if (fseek(fp, offset, cwhence) != 0) {\n    return inject_io_err_inner(net, new_port(NUM, new_i24(ferror(fp))));\n  }\n\n  return inject_ok(net, new_port(ERA, 0));\n}\n\n// Returns the current time as a tuple of the high\n// and low 24 bits of a 48-bit nanosecond timestamp.\n// Returns: Result<(u24, u24), IOError<*>>\nPort io_get_time(Net* net, Book* book, Port argm) {\n  // Get the current time in nanoseconds\n  u64 time_ns = time64();\n  // Encode the time as a 64-bit unsigned integer\n  u32 time_hi = (u32)(time_ns >> 24) & 0xFFFFFFF;\n  u32 time_lo = (u32)(time_ns & 0xFFFFFFF);\n  // Allocate a node to store the time\n  u32 lps = 0;\n  u32 loc = node_alloc_1(net, tm[0], &lps);\n  node_create(net, loc, new_pair(new_port(NUM, new_u24(time_hi)), new_port(NUM, new_u24(time_lo))));\n\n  return inject_ok(net, new_port(CON, loc));\n}\n\n// Sleeps.\n// `argm` is a tuple (CON node) of the high and low\n// 24 bits for a 48-bit duration in nanoseconds.\n// Returns: Result<*, IOError<*>>\nPort io_sleep(Net* net, Book* book, Port argm) {\n  Tup tup = readback_tup(net, book, argm, 2);\n  if (tup.elem_len != 2) {\n    return inject_io_err_type(net);\n  }\n\n  // Get the sleep duration node\n  Pair dur_node = node_load(net, get_val(argm));\n  // Get the high and low 24-bit parts of the duration\n  u32 dur_hi = get_u24(get_val(tup.elem_buf[0]));\n  u32 dur_lo = get_u24(get_val(tup.elem_buf[1]));\n  // Combine into a 48-bit duration in nanoseconds\n  u64 dur_ns = (((u64)dur_hi) << 24) | dur_lo;\n  // Sleep for the specified duration\n  struct timespec ts;\n  ts.tv_sec = dur_ns / 1000000000;\n  ts.tv_nsec = dur_ns % 1000000000;\n  nanosleep(&ts, NULL);\n\n  return inject_ok(net, new_port(ERA, 0));\n}\n\n// Opens a dylib at the provided path.\n// `argm` is a tuple of `filename` and `lazy`.\n// `filename` is a λ-encoded string.\n// `lazy` is a `bool` indicating if functions should be lazily loaded.\n// Returns: Result<Dylib, IOError<String>>\nPort io_dl_open(Net* net, Book* book, Port argm) {\n  Tup tup = readback_tup(net, book, argm, 2);\n  Str str = readback_str(net, book, tup.elem_buf[0]);\n  u32 lazy = get_u24(get_val(tup.elem_buf[1]));\n\n  int flags = lazy ? RTLD_LAZY : RTLD_NOW;\n\n  for (u32 dl = 0; dl < sizeof(DYLIBS); dl++) {\n    if (DYLIBS[dl] == NULL) {\n      DYLIBS[dl] = dlopen(str.buf, flags);\n\n      free(str.buf);\n\n      if (DYLIBS[dl] == NULL) {\n        return inject_io_err_str(net, dlerror());\n      }\n\n      return inject_ok(net, new_port(NUM, new_u24(dl)));\n    }\n  }\n\n  return inject_io_err_str(net, \"too many open dylibs\");\n}\n\n// Calls a function from a loaded dylib.\n// `argm` is a 3-tuple of `dylib_handle`, `symbol`, `args`.\n// `dylib_handle` is the numeric node returned from a `DL_OPEN` call.\n// `symbol` is a λ-encoded string of the symbol name.\n// `args` is the argument to be provided to the dylib symbol.\n//\n// This function returns a Result with an Ok variant containing an\n// arbitrary type.\n//\n// Returns Result<T, IOError<String>>\nPort io_dl_call(Net* net, Book* book, Port argm) {\n  Tup tup = readback_tup(net, book, argm, 3);\n  if (tup.elem_len != 3) {\n    return inject_io_err_type(net);\n  }\n\n  void* dl = readback_dylib(tup.elem_buf[0]);\n  Str symbol = readback_str(net, book, tup.elem_buf[1]);\n\n  dlerror();\n  Port (*func)(Net*, Book*, Port) = dlsym(dl, symbol.buf);\n  char* error = dlerror();\n  if (error != NULL) {\n    return inject_io_err_str(net, error);\n  }\n\n  return inject_ok(net, func(net, book, tup.elem_buf[2]));\n}\n\n// Closes a loaded dylib, reclaiming the handle.\n//\n// Returns:  Result<*, IOError<String>>\nPort io_dl_close(Net* net, Book* book, Port argm) {\n  void* dl = readback_dylib(argm);\n  if (dl == NULL) {\n    return inject_io_err_type(net);\n  }\n\n  int err = dlclose(dl) != 0;\n  if (err != 0) {\n    return inject_io_err_str(net, dlerror());\n  }\n\n  DYLIBS[get_u24(get_val(argm))] = NULL;\n\n  return inject_ok(net, new_port(ERA, 0));\n}\n\n// Book Loader\n// -----------\n\nvoid book_init(Book* book) {\n  book->ffns_buf[book->ffns_len++] = (FFn){\"READ\", io_read};\n  book->ffns_buf[book->ffns_len++] = (FFn){\"OPEN\", io_open};\n  book->ffns_buf[book->ffns_len++] = (FFn){\"CLOSE\", io_close};\n  book->ffns_buf[book->ffns_len++] = (FFn){\"FLUSH\", io_flush};\n  book->ffns_buf[book->ffns_len++] = (FFn){\"WRITE\", io_write};\n  book->ffns_buf[book->ffns_len++] = (FFn){\"SEEK\", io_seek};\n  book->ffns_buf[book->ffns_len++] = (FFn){\"GET_TIME\", io_get_time};\n  book->ffns_buf[book->ffns_len++] = (FFn){\"SLEEP\", io_sleep};\n  book->ffns_buf[book->ffns_len++] = (FFn){\"DL_OPEN\", io_dl_open};\n  book->ffns_buf[book->ffns_len++] = (FFn){\"DL_CALL\", io_dl_call};\n  book->ffns_buf[book->ffns_len++] = (FFn){\"DL_CLOSE\", io_dl_open};\n}\n\n// Monadic IO Evaluator\n// ---------------------\n\n// Runs an IO computation.\nvoid do_run_io(Net* net, Book* book, Port port) {\n  book_init(book);\n\n  setlinebuf(stdout);\n  setlinebuf(stderr);\n\n  // IO loop\n  while (true) {\n    // Normalizes the net\n    normalize(net, book);\n\n    // Reads the λ-Encoded Ctr\n    Ctr ctr = readback_ctr(net, book, peek(net, port));\n\n    // Checks if IO Magic Number is a CON\n    if (ctr.args_len < 1 || get_tag(ctr.args_buf[0]) != CON) {\n      break;\n    }\n\n    // Checks the IO Magic Number\n    Pair io_magic = node_load(net, get_val(ctr.args_buf[0]));\n    //printf(\"%08x %08x\\n\", get_u24(get_val(get_fst(io_magic))), get_u24(get_val(get_snd(io_magic))));\n    if (get_val(get_fst(io_magic)) != new_u24(IO_MAGIC_0) || get_val(get_snd(io_magic)) != new_u24(IO_MAGIC_1)) {\n      break;\n    }\n\n    switch (ctr.tag) {\n      case IO_CALL: {\n        if (ctr.args_len != 4) {\n          fprintf(stderr, \"invalid IO_CALL: args_len = %u\\n\", ctr.args_len);\n          break;\n        }\n\n        Str  func = readback_str(net, book, ctr.args_buf[1]);\n        FFn* ffn  = NULL;\n        // FIXME: optimize this linear search\n        for (u32 fid = 0; fid < book->ffns_len; ++fid) {\n          if (strcmp(func.buf, book->ffns_buf[fid].name) == 0) {\n            ffn = &book->ffns_buf[fid];\n            break;\n          }\n        }\n\n        free(func.buf);\n\n        Port argm = ctr.args_buf[2];\n        Port cont = ctr.args_buf[3];\n\n        Port ret;\n        if (ffn == NULL) {\n          ret = inject_io_err_name(net);\n        } else {\n          ret = ffn->func(net, book, argm);\n        };\n\n        u32 lps = 0;\n        u32 loc = node_alloc_1(net, tm[0], &lps);\n        node_create(net, loc, new_pair(ret, ROOT));\n        boot_redex(net, new_pair(new_port(CON, loc), cont));\n        port = ROOT;\n\n        continue;\n      }\n\n      case IO_DONE: {\n        break;\n      }\n    }\n    break;\n  }\n}\n"
  },
  {
    "path": "src/run.cu",
    "content": "#include <dlfcn.h>\n#include <errno.h>\n#include <stdio.h>\n#include \"hvm.cu\"\n\n// Readback: λ-Encoded Ctr\nstruct Ctr {\n  u32  tag;\n  u32  args_len;\n  Port args_buf[16];\n};\n\n// Readback: Tuples\nstruct Tup {\n  u32  elem_len;\n  Port elem_buf[8];\n};\n\n// Readback: λ-Encoded Str (UTF-32)\n// FIXME: this is actually ASCII :|\nstruct Str {\n  u32  len;\n  char* buf;\n};\n\n// Readback: λ-Encoded list of bytes\ntypedef struct Bytes {\n  u32  len;\n  char *buf;\n} Bytes;\n\n// IO Magic Number\n#define IO_MAGIC_0 0xD0CA11\n#define IO_MAGIC_1 0xFF1FF1\n\n// IO Tags\n#define IO_DONE 0\n#define IO_CALL 1\n\n// Result Tags\n#define RESULT_OK  0\n#define RESULT_ERR 1\n\n// IOError = {\n//   Type,           -- a type error\n//   Name,           -- invalid io func name\n//   Inner {val: T}, -- an error while calling an io func\n// }\n#define IO_ERR_TYPE 0\n#define IO_ERR_NAME 1\n#define IO_ERR_INNER 2\n\ntypedef struct IOError {\n  u32 tag;\n  Port val;\n} IOError;\n\n// List Type\n#define LIST_NIL  0\n#define LIST_CONS 1\n\n// Readback\n// --------\n\n// Reads back a λ-Encoded constructor from device to host.\n// Encoding: λt ((((t TAG) arg0) arg1) ...)\nCtr gnet_readback_ctr(GNet* gnet, Port port) {\n  Ctr ctr;\n  ctr.tag = -1;\n  ctr.args_len = 0;\n\n  // Loads root lambda\n  Port lam_port = gnet_expand(gnet, port);\n  if (get_tag(lam_port) != CON) return ctr;\n  Pair lam_node = gnet_node_load(gnet, get_val(lam_port));\n\n  // Loads first application\n  Port app_port = gnet_expand(gnet, get_fst(lam_node));\n  if (get_tag(app_port) != CON) return ctr;\n  Pair app_node = gnet_node_load(gnet, get_val(app_port));\n\n  // Loads first argument (as the tag)\n  Port arg_port = gnet_expand(gnet, get_fst(app_node));\n  if (get_tag(arg_port) != NUM) return ctr;\n  ctr.tag = get_u24(get_val(arg_port));\n\n  // Loads remaining arguments\n  while (true) {\n    app_port = gnet_expand(gnet, get_snd(app_node));\n    if (get_tag(app_port) != CON) break;\n    app_node = gnet_node_load(gnet, get_val(app_port));\n    arg_port = gnet_expand(gnet, get_fst(app_node));\n    ctr.args_buf[ctr.args_len++] = arg_port;\n  }\n\n  return ctr;\n}\n\n// Reads back a tuple of at most `size` elements. Tuples are\n// (right-nested con nodes) (CON 1 (CON 2 (CON 3 (...))))\n// The provided `port` should be `expanded` before calling.\nextern \"C\" Tup gnet_readback_tup(GNet* gnet, Port port, u32 size) {\n  Tup tup;\n  tup.elem_len = 0;\n\n  // Loads remaining arguments\n  while (get_tag(port) == CON && (tup.elem_len + 1 < size)) {\n    Pair node = gnet_node_load(gnet, get_val(port));\n    tup.elem_buf[tup.elem_len++] = gnet_expand(gnet, get_fst(node));\n\n    port = gnet_expand(gnet, get_snd(node));\n  }\n\n  tup.elem_buf[tup.elem_len++] = port;\n\n  return tup;\n}\n\n\n// Converts a Port into a list of bytes.\n// Encoding:\n// - λt (t NIL)\n// - λt (((t CONS) head) tail)\nextern \"C\" Bytes gnet_readback_bytes(GNet* gnet, Port port) {\n  // Result\n  Bytes bytes;\n  u32 capacity = 256;\n  bytes.buf = (char*) malloc(sizeof(char) * capacity);\n  bytes.len = 0;\n\n  // Readback loop\n  while (true) {\n    // Normalizes the net\n    gnet_normalize(gnet);\n\n    // Reads the λ-Encoded Ctr\n    Ctr ctr = gnet_readback_ctr(gnet, gnet_peek(gnet, port));\n\n    // Reads string layer\n    switch (ctr.tag) {\n      case LIST_NIL: {\n        break;\n      }\n      case LIST_CONS: {\n        if (ctr.args_len != 2) break;\n        if (get_tag(ctr.args_buf[0]) != NUM) break;\n\n        if (bytes.len == capacity - 1) {\n          capacity *= 2;\n          bytes.buf = (char*) realloc(bytes.buf, capacity);\n        }\n\n        bytes.buf[bytes.len++] = get_u24(get_val(ctr.args_buf[0]));\n        gnet_boot_redex(gnet, new_pair(ctr.args_buf[1], ROOT));\n        port = ROOT;\n        continue;\n      }\n    }\n    break;\n  }\n\n  return bytes;\n}\n\n// Reads back a UTF-32 (truncated to 24 bits) string.\n// Since unicode scalars can fit in 21 bits, HVM's u24\n// integers can contain any unicode scalar value.\n// Encoding:\n// - λt (t NIL)\n// - λt (((t CONS) head) tail)\nextern \"C\" Str gnet_readback_str(GNet* gnet, Port port) {\n  // gnet_readback_bytes is guaranteed to return a buffer with a capacity of at least one more\n  // than the number of bytes read, so we can null-terminate it.\n  Bytes bytes = gnet_readback_bytes(gnet, port);\n\n  Str str;\n  str.len = bytes.len;\n  str.buf = bytes.buf;\n  str.buf[str.len] = 0;\n\n  return str;\n}\n\n\n/// Returns a λ-Encoded Ctr for a NIL: λt (t NIL)\n/// Should only be called within `inject_bytes`, as a previous call\n/// to `get_resources` is expected.\n__device__ Port inject_nil(Net* net, TM* tm) {\n  u32 v1 = tm->vloc[0];\n\n  u32 n1 = tm->nloc[0];\n  u32 n2 = tm->nloc[1];\n\n  vars_create(net, v1, NONE);\n  Port var = new_port(VAR, v1);\n\n  node_create(net, n1, new_pair(new_port(NUM, new_u24(LIST_NIL)), var));\n  node_create(net, n2, new_pair(new_port(CON, n1), var));\n\n  return new_port(CON, n2);\n}\n\n/// Returns a λ-Encoded Ctr for a CONS: λt (((t CONS) head) tail)\n/// Should only be called within `inject_bytes`, as a previous call\n/// to `get_resources` is expected.\n/// The `char_idx` parameter is used to offset the vloc and nloc\n/// allocations, otherwise they would conflict with each other on\n/// subsequent calls.\n__device__ Port inject_cons(Net* net, TM* tm, Port head, Port tail) {\n  u32 v1 = tm->vloc[0];\n\n  u32 n1 = tm->nloc[0];\n  u32 n2 = tm->nloc[1];\n  u32 n3 = tm->nloc[2];\n  u32 n4 = tm->nloc[3];\n\n  vars_create(net, v1, NONE);\n  Port var = new_port(VAR, v1);\n\n  node_create(net, n1, new_pair(tail, var));\n  node_create(net, n2, new_pair(head, new_port(CON, n1)));\n  node_create(net, n3, new_pair(new_port(NUM, new_u24(LIST_CONS)), new_port(CON, n2)));\n  node_create(net, n4, new_pair(new_port(CON, n3), var));\n\n  return new_port(CON, n4);\n}\n\n// Converts a list of bytes to a Port.\n// Encoding:\n// - λt (t NIL)\n// - λt (((t CONS) head) tail)\n__device__ Port inject_bytes(Net* net, TM* tm, Bytes *bytes) {\n  // Allocate all resources up front:\n  // - NIL needs  2 nodes & 1 var\n  // - CONS needs 4 nodes & 1 var\n  u32 len = bytes->len;\n\n  if (!get_resources(net, tm, 0, 2, 1)) {\n    return new_port(ERA, 0);\n  }\n  Port port = inject_nil(net, tm);\n\n  for (u32 i = 0; i < len; i++) {\n    if (!get_resources(net, tm, 0, 4, 1)) {\n      return new_port(ERA, 0);\n    }\n\n    Port byte = new_port(NUM, new_u24(bytes->buf[len - i - 1]));\n    port = inject_cons(net, tm, byte, port);\n  }\n\n  return port;\n}\n\n__global__ void make_bytes_port(GNet* gnet, Bytes bytes, Port* ret) {\n  if (GID() == 0) {\n    TM tm = tmem_new();\n    Net net = vnet_new(gnet, NULL, gnet->turn);\n    *ret = inject_bytes(&net, &tm, &bytes);\n  }\n}\n\n// Converts a list of bytes to a Port.\n// Encoding:\n// - λt (t NIL)\n// - λt (((t CONS) head) tail)\nextern \"C\" Port gnet_inject_bytes(GNet* gnet, Bytes *bytes) {\n  Port* d_ret;\n  cudaMalloc(&d_ret, sizeof(Port));\n\n  Bytes bytes_cu;\n  bytes_cu.len = bytes->len;\n\n  cudaMalloc(&bytes_cu.buf, sizeof(char) * bytes_cu.len);\n  cudaMemcpy(bytes_cu.buf, bytes->buf, sizeof(char) * bytes_cu.len, cudaMemcpyHostToDevice);\n\n  make_bytes_port<<<1,1>>>(gnet, bytes_cu, d_ret);\n\n  Port ret;\n  cudaMemcpy(&ret, d_ret, sizeof(Port), cudaMemcpyDeviceToHost);\n  cudaFree(d_ret);\n  cudaFree(bytes_cu.buf);\n\n  return ret;\n}\n\n/// Returns a λ-Encoded Ctr for a RESULT_OK: λt ((t RESULT_OK) val)\n__device__ Port inject_ok(Net* net, TM* tm, Port val) {\n  if (!get_resources(net, tm, 0, 3, 1)) {\n    printf(\"inject_ok: failed to get resources\\n\");\n    return new_port(ERA, 0);\n  }\n\n  u32 v1 = tm->vloc[0];\n\n  u32 n1 = tm->nloc[0];\n  u32 n2 = tm->nloc[1];\n  u32 n3 = tm->nloc[2];\n\n  vars_create(net, v1, NONE);\n  Port var = new_port(VAR, v1);\n\n  node_create(net, n1, new_pair(val, var));\n  node_create(net, n2, new_pair(new_port(NUM, new_u24(RESULT_OK)), new_port(CON, n1)));\n  node_create(net, n3, new_pair(new_port(CON, n2), var));\n\n  return new_port(CON, n3);\n}\n\n__global__ void make_ok_port(GNet* gnet, Port val, Port* ret) {\n  if (GID() == 0) {\n    TM tm = tmem_new();\n    Net net = vnet_new(gnet, NULL, gnet->turn);\n    *ret = inject_ok(&net, &tm, val);\n  }\n}\n\nextern \"C\" Port gnet_inject_ok(GNet* gnet, Port val) {\n  Port* d_ret;\n  cudaMalloc(&d_ret, sizeof(Port));\n\n  make_ok_port<<<1,1>>>(gnet, val, d_ret);\n\n  Port ret;\n  cudaMemcpy(&ret, d_ret, sizeof(Port), cudaMemcpyDeviceToHost);\n  cudaFree(d_ret);\n\n  return ret;\n}\n\n/// Returns a λ-Encoded Ctr for a RESULT_ERR: λt ((t RESULT_ERR) err)\n__device__ Port inject_err(Net* net, TM* tm, Port err) {\n  if (!get_resources(net, tm, 0, 3, 1)) {\n    printf(\"inject_err: failed to get resources\\n\");\n    return new_port(ERA, 0);\n  }\n\n  u32 v1 = tm->vloc[0];\n\n  u32 n1 = tm->nloc[0];\n  u32 n2 = tm->nloc[1];\n  u32 n3 = tm->nloc[2];\n\n  vars_create(net, v1, NONE);\n  Port var = new_port(VAR, v1);\n\n  node_create(net, n1, new_pair(err, var));\n  node_create(net, n2, new_pair(new_port(NUM, new_u24(RESULT_ERR)), new_port(CON, n1)));\n  node_create(net, n3, new_pair(new_port(CON, n2), var));\n\n  return new_port(CON, n3);\n}\n\n\n__global__ void make_err_port(GNet* gnet, Port val, Port* ret) {\n  if (GID() == 0) {\n    TM tm = tmem_new();\n    Net net = vnet_new(gnet, NULL, gnet->turn);\n    *ret = inject_err(&net, &tm, val);\n  }\n}\n\nextern \"C\" Port gnet_inject_err(GNet* gnet, Port val) {\n  Port* d_ret;\n  cudaMalloc(&d_ret, sizeof(Port));\n\n  make_err_port<<<1,1>>>(gnet, val, d_ret);\n\n  Port ret;\n  cudaMemcpy(&ret, d_ret, sizeof(Port), cudaMemcpyDeviceToHost);\n  cudaFree(d_ret);\n\n  return ret;\n}\n\n/// Returns a λ-Encoded Ctr for a Result/Err(IOError(..))\n__device__ Port inject_io_err(Net* net, TM* tm, IOError err) {\n  if (err.tag <= IO_ERR_NAME) {\n    if (!get_resources(net, tm, 0, 2, 1)) {\n      return new_port(ERA, 0);\n    }\n\n    u32 v1 = tm->vloc[0];\n\n    u32 n1 = tm->nloc[0];\n    u32 n2 = tm->nloc[1];\n\n    vars_create(net, v1, NONE);\n    Port var = new_port(VAR, v1);\n\n    node_create(net, n1, new_pair(new_port(NUM, new_u24(err.tag)), var));\n    node_create(net, n2, new_pair(new_port(CON, n1), var));\n\n    return inject_err(net, tm, new_port(CON, n2));\n  }\n\n  if (!get_resources(net, tm, 0, 3, 1)) {\n    return new_port(ERA, 0);\n  }\n\n  u32 v1 = tm->vloc[0];\n\n  u32 n1 = tm->nloc[0];\n  u32 n2 = tm->nloc[1];\n  u32 n3 = tm->nloc[2];\n\n  vars_create(net, v1, NONE);\n  Port var = new_port(VAR, v1);\n\n  node_create(net, n1, new_pair(err.val, var));\n  node_create(net, n2, new_pair(new_port(NUM, new_u24(IO_ERR_INNER)), new_port(CON, n1)));\n  node_create(net, n3, new_pair(new_port(CON, n2), var));\n\n  return inject_err(net, tm, new_port(CON, n3));\n}\n\n__global__ void make_io_err_port(GNet* gnet, IOError err, Port* ret) {\n  if (GID() == 0) {\n    TM tm = tmem_new();\n    Net net = vnet_new(gnet, NULL, gnet->turn);\n    *ret = inject_io_err(&net, &tm, err);\n  }\n}\n\nextern \"C\" Port gnet_inject_io_err(GNet* gnet, IOError err) {\n  Port* d_ret;\n  cudaMalloc(&d_ret, sizeof(Port));\n\n  make_io_err_port<<<1,1>>>(gnet, err, d_ret);\n\n  Port ret;\n  cudaMemcpy(&ret, d_ret, sizeof(Port), cudaMemcpyDeviceToHost);\n  cudaFree(d_ret);\n\n  return ret;\n}\n\n/// Returns a λ-Encoded Ctr for a Result/Err(IOError/Type)\nextern \"C\" Port gnet_inject_io_err_type(GNet* gnet) {\n  IOError io_error = {\n    .tag = IO_ERR_TYPE,\n  };\n\n  return gnet_inject_io_err(gnet, io_error);\n}\n\n/// Returns a λ-Encoded Ctr for a Result/Err(IOError/Name)\nextern \"C\" Port gnet_inject_io_err_name(GNet* gnet) {\n  IOError io_error = {\n    .tag = IO_ERR_NAME,\n  };\n\n  return gnet_inject_io_err(gnet, io_error);\n}\n\n/// Returns a λ-Encoded Ctr for a Result/Err(IOError/Inner(val))\nextern \"C\" Port gnet_inject_io_err_inner(GNet* gnet, Port val) {\n  IOError io_error = {\n    .tag = IO_ERR_INNER,\n    .val = val,\n  };\n\n  return gnet_inject_io_err(gnet, io_error);\n}\n\n/// Returns a λ-Encoded Ctr for an Result<T, IOError<String>>\n/// `err` must be `NUL`-terminated.\nextern \"C\" Port gnet_inject_io_err_str(GNet* gnet, char* err) {\n  Port* d_bytes_port;\n  cudaMalloc(&d_bytes_port, sizeof(Port));\n\n  Bytes bytes_cu;\n  bytes_cu.len = strlen(err);\n\n  cudaMalloc(&bytes_cu.buf, sizeof(char) * bytes_cu.len);\n  cudaMemcpy(bytes_cu.buf, err, sizeof(char) * bytes_cu.len, cudaMemcpyHostToDevice);\n\n  make_bytes_port<<<1,1>>>(gnet, bytes_cu, d_bytes_port);\n\n  Port bytes_port;\n  cudaMemcpy(&bytes_port, d_bytes_port, sizeof(Port), cudaMemcpyDeviceToHost);\n  cudaFree(d_bytes_port);\n\n  cudaFree(bytes_cu.buf);\n\n  return gnet_inject_io_err_inner(gnet, bytes_port);\n}\n\n\n// Primitive IO Fns\n// -----------------\n\n// Open file pointers. Indices into this array\n// are used as \"file descriptors\".\n// Indices 0 1 and 2 are reserved.\n// - 0 -> stdin\n// - 1 -> stdout\n// - 2 -> stderr\nstatic FILE* FILE_POINTERS[256];\n\n// Open dylibs handles. Indices into this array\n// are used as opaque loadedd object \"handles\".\nstatic void* DYLIBS[256];\n\n// Converts a NUM port (file descriptor) to file pointer.\nFILE* readback_file(Port port) {\n  if (get_tag(port) != NUM) {\n    fprintf(stderr, \"non-num where file descriptor was expected: %s\\n\", show_port(port).x);\n    return NULL;\n  }\n\n  u32 idx = get_u24(get_val(port));\n\n  if (idx == 0) return stdin;\n  if (idx == 1) return stdout;\n  if (idx == 2) return stderr;\n\n  FILE* fp = FILE_POINTERS[idx];\n  if (fp == NULL) {\n    fprintf(stderr, \"invalid file descriptor\\n\");\n    return NULL;\n  }\n\n  return fp;\n}\n\n// Converts a NUM port (dylib handle) to an opaque dylib object.\nvoid* readback_dylib(Port port) {\n  if (get_tag(port) != NUM) {\n    fprintf(stderr, \"non-num where dylib handle was expected: %i\\n\", get_tag(port));\n    return NULL;\n  }\n\n  u32 idx = get_u24(get_val(port));\n\n  void* dl = DYLIBS[idx];\n  if (dl == NULL) {\n    fprintf(stderr, \"invalid dylib handle\\n\");\n    return NULL;\n  }\n\n  return dl;\n}\n\n// Reads from a file a specified number of bytes.\n// `argm` is a tuple of (file_descriptor, num_bytes).\n// Returns: Result<Bytes, IOError<i24>>\nPort io_read(GNet* gnet, Port argm) {\n  Tup tup = gnet_readback_tup(gnet, argm, 2);\n  if (tup.elem_len != 2) {\n    fprintf(stderr, \"io_read: expected 2-tuple\\n\");\n    return gnet_inject_io_err_type(gnet);\n  }\n\n  FILE* fp = readback_file(tup.elem_buf[0]);\n  u32 num_bytes = get_u24(get_val(tup.elem_buf[1]));\n\n  if (fp == NULL) {\n    return gnet_inject_io_err_inner(gnet, new_port(NUM, new_i24(EBADF)));\n  }\n\n  /// Read a string.\n  Bytes bytes;\n  bytes.buf = (char*) malloc(sizeof(char) * num_bytes);\n  bytes.len = fread(bytes.buf, sizeof(char), num_bytes, fp);\n\n  if ((bytes.len != num_bytes) && ferror(fp)) {\n    fprintf(stderr, \"io_read: failed to read\\n\");\n    free(bytes.buf);\n    return gnet_inject_io_err_inner(gnet, new_port(NUM, new_i24(ferror(fp))));\n  }\n\n  // Convert it to a port.\n  Port ret = gnet_inject_bytes(gnet, &bytes);\n  free(bytes.buf);\n\n  return gnet_inject_ok(gnet, ret);\n}\n\n// Opens a file with the provided mode.\n// `argm` is a tuple (CON node) of the\n// file name and mode as strings.\n// Returns: Result<File, IOError<i24>>\nPort io_open(GNet* gnet, Port argm) {\n  Tup tup = gnet_readback_tup(gnet, argm, 2);\n  if (tup.elem_len != 2) {\n    return gnet_inject_io_err_type(gnet);\n  }\n\n  Str name = gnet_readback_str(gnet, tup.elem_buf[0]);\n  Str mode = gnet_readback_str(gnet, tup.elem_buf[1]);\n\n  for (u32 fd = 3; fd < sizeof(FILE_POINTERS); fd++) {\n    if (FILE_POINTERS[fd] == NULL) {\n      FILE_POINTERS[fd] = fopen(name.buf, mode.buf);\n\n      free(name.buf);\n      free(mode.buf);\n\n      if (FILE_POINTERS[fd] == NULL) {\n        return gnet_inject_io_err_inner(gnet, new_port(NUM, new_i24(errno)));\n      }\n\n      return gnet_inject_ok(gnet, new_port(NUM, new_u24(fd)));\n    }\n  }\n\n  free(name.buf);\n  free(mode.buf);\n\n  // too many open files\n  return gnet_inject_io_err_inner(gnet, new_port(NUM, new_i24(EMFILE)));\n}\n\n// Closes a file, reclaiming the file descriptor.\n// Returns: Result<*, IOError<i24>>\nPort io_close(GNet* gnet, Port argm) {\n  FILE* fp = readback_file(argm);\n  if (fp == NULL) {\n    return gnet_inject_io_err_inner(gnet, new_port(NUM, new_i24(EBADF)));\n  }\n\n  if (fclose(fp) != 0) {\n    return gnet_inject_io_err_inner(gnet, new_port(NUM, new_i24(ferror(fp))));\n  }\n\n  FILE_POINTERS[get_u24(get_val(argm))] = NULL;\n\n  return gnet_inject_ok(gnet, new_port(ERA, 0));\n}\n\n// Writes a list of bytes to a file.\n// `argm` is a tuple (CON node) of the\n// file descriptor and list of bytes to write.\n// Returns: Result<*, IOError<i24>>\nPort io_write(GNet* gnet, Port argm) {\n  Tup tup = gnet_readback_tup(gnet, argm, 2);\n  if (tup.elem_len != 2) {\n    return gnet_inject_io_err_type(gnet);\n  }\n\n  FILE* fp = readback_file(tup.elem_buf[0]);\n  Bytes bytes = gnet_readback_bytes(gnet, tup.elem_buf[1]);\n\n  if (fp == NULL) {\n    free(bytes.buf);\n\n    return gnet_inject_io_err_inner(gnet, new_port(NUM, new_i24(EBADF)));\n  }\n\n  if (fwrite(bytes.buf, sizeof(char), bytes.len, fp) != bytes.len) {\n    free(bytes.buf);\n\n    return gnet_inject_io_err_inner(gnet, new_port(NUM, new_i24(ferror(fp))));\n  }\n\n  free(bytes.buf);\n\n  return gnet_inject_ok(gnet, new_port(ERA, 0));\n}\n\n// Flushes an output stream.\n// Returns: Result<*, IOError<i24>>\nPort io_flush(GNet* gnet, Port argm) {\n  FILE* fp = readback_file(argm);\n  if (fp == NULL) {\n    return gnet_inject_io_err_inner(gnet, new_port(NUM, new_i24(EBADF)));\n  }\n\n  if (fflush(fp) != 0) {\n    return gnet_inject_io_err_inner(gnet, new_port(NUM, new_i24(ferror(fp))));\n  }\n\n  return gnet_inject_ok(gnet, new_port(ERA, 0));\n}\n\n// Seeks to a position in a file.\n// `argm` is a 3-tuple (CON fd (CON offset whence)), where\n// - fd is a file descriptor\n// - offset is a signed byte offset\n// - whence is what that offset is relative to:\n//    - 0 (SEEK_SET): beginning of file\n//    - 1 (SEEK_CUR): current position of the file pointer\n//    - 2 (SEEK_END): end of the file\n// Returns: Result<*, IOError<i24>>\nPort io_seek(GNet* gnet, Port argm) {\n  Tup tup = gnet_readback_tup(gnet, argm, 3);\n  if (tup.elem_len != 3) {\n    fprintf(stderr, \"io_seek: expected 3-tuple\\n\");\n    return gnet_inject_io_err_type(gnet);\n  }\n\n  FILE* fp = readback_file(tup.elem_buf[0]);\n  i32 offset = get_i24(get_val(tup.elem_buf[1]));\n  u32 whence = get_i24(get_val(tup.elem_buf[2]));\n\n  if (fp == NULL) {\n    return gnet_inject_io_err_inner(gnet, new_port(NUM, new_i24(EBADF)));\n  }\n\n  int cwhence;\n  switch (whence) {\n    case 0: cwhence = SEEK_SET; break;\n    case 1: cwhence = SEEK_CUR; break;\n    case 2: cwhence = SEEK_END; break;\n    default:\n      return gnet_inject_io_err_type(gnet);\n  }\n\n  if (fseek(fp, offset, cwhence) != 0) {\n    return gnet_inject_io_err_inner(gnet, new_port(NUM, new_i24(ferror(fp))));\n  }\n\n  return gnet_inject_ok(gnet, new_port(ERA, 0));\n}\n\n// Returns the current time as a tuple of the high\n// and low 24 bits of a 48-bit nanosecond timestamp.\n// Returns: Result<(u24, u24), IOError<*>>\nPort io_get_time(GNet* gnet, Port argm) {\n  // Get the current time in nanoseconds\n  u64 time_ns = time64();\n  // Encode the time as a 64-bit unsigned integer\n  u32 time_hi = (u32)(time_ns >> 24) & 0xFFFFFFF;\n  u32 time_lo = (u32)(time_ns & 0xFFFFFFF);\n  // Return the encoded time\n  return gnet_make_node(gnet, CON, new_port(NUM, new_u24(time_hi)), new_port(NUM, new_u24(time_lo)));\n}\n\n// Sleeps.\n// `argm` is a tuple (CON node) of the high and low\n// 24 bits for a 48-bit duration in nanoseconds.\n// Returns: Result<*, IOError<*>>\nPort io_sleep(GNet* gnet, Port argm) {\n  Tup tup = gnet_readback_tup(gnet, argm, 2);\n  if (tup.elem_len != 2) {\n    return gnet_inject_io_err_type(gnet);\n  }\n\n  // Get the sleep duration node\n  Pair dur_node = gnet_node_load(gnet, get_val(argm));\n  // Get the high and low 24-bit parts of the duration\n  u32 dur_hi = get_u24(get_val(tup.elem_buf[0]));\n  u32 dur_lo = get_u24(get_val(tup.elem_buf[1]));\n  // Combine into a 48-bit duration in nanoseconds\n  u64 dur_ns = (((u64)dur_hi) << 24) | dur_lo;\n  // Sleep for the specified duration\n  struct timespec ts;\n  ts.tv_sec = dur_ns / 1000000000;\n  ts.tv_nsec = dur_ns % 1000000000;\n  nanosleep(&ts, NULL);\n\n  return gnet_inject_ok(gnet, new_port(ERA, 0));\n}\n\n// Opens a dylib at the provided path.\n// `argm` is a tuple of `filename` and `lazy`.\n// `filename` is a λ-encoded string.\n// `lazy` is a `bool` indicating if functions should be lazily loaded.\n// Returns: Result<Dylib, IOError<String>>\nPort io_dl_open(GNet* gnet, Port argm) {\n  Tup tup = gnet_readback_tup(gnet, argm, 2);\n  Str str = gnet_readback_str(gnet, tup.elem_buf[0]);\n  u32 lazy = get_u24(get_val(tup.elem_buf[1]));\n\n  int flags = lazy ? RTLD_LAZY : RTLD_NOW;\n\n  for (u32 dl = 0; dl < sizeof(DYLIBS); dl++) {\n    if (DYLIBS[dl] == NULL) {\n      DYLIBS[dl] = dlopen(str.buf, flags);\n\n      free(str.buf);\n\n      if (DYLIBS[dl] == NULL) {\n        return gnet_inject_io_err_str(gnet, dlerror());\n      }\n\n      return gnet_inject_ok(gnet, new_port(NUM, new_u24(dl)));\n    }\n  }\n\n  return gnet_inject_io_err_str(gnet, \"too many open dylibs\");\n}\n\n// Calls a function from a loaded dylib.\n// `argm` is a 3-tuple of `dylib_handle`, `symbol`, `args`.\n// `dylib_handle` is the numeric node returned from a `DL_OPEN` call.\n// `symbol` is a λ-encoded string of the symbol name.\n// `args` is the argument to be provided to the dylib symbol.\n//\n// This function returns a Result with an Ok variant containing an\n// arbitrary type.\n//\n// Returns Result<T, IOError<String>>\nPort io_dl_call(GNet* gnet, Port argm) {\n  Tup tup = gnet_readback_tup(gnet, argm, 3);\n  if (tup.elem_len != 3) {\n    fprintf(stderr, \"io_dl_call: expected 3-tuple\\n\");\n\n    return gnet_inject_io_err_type(gnet);\n  }\n\n  void* dl = readback_dylib(tup.elem_buf[0]);\n  Str symbol = gnet_readback_str(gnet, tup.elem_buf[1]);\n\n  dlerror();\n  Port (*func)(GNet*, Port) = (Port (*)(GNet*, Port)) dlsym(dl, symbol.buf);\n  char* error = dlerror();\n  if (error != NULL) {\n    return gnet_inject_io_err_str(gnet, error);\n  }\n\n  return gnet_inject_ok(gnet, func(gnet, tup.elem_buf[2]));\n}\n\n// Closes a loaded dylib, reclaiming the handle.\n//\n// Returns:  Result<*, IOError<String>>\nPort io_dl_close(GNet* gnet, Book* book, Port argm) {\n  void* dl = readback_dylib(argm);\n  if (dl == NULL) {\n    fprintf(stderr, \"io_dl_close: invalid handle\\n\");\n\n    return gnet_inject_io_err_type(gnet);\n  }\n\n  int err = dlclose(dl) != 0;\n  if (err != 0) {\n    return gnet_inject_io_err_str(gnet, dlerror());\n  }\n\n  DYLIBS[get_u24(get_val(argm))] = NULL;\n\n  return gnet_inject_ok(gnet, new_port(ERA, 0));\n}\n\nvoid book_init(Book* book) {\n  book->ffns_buf[book->ffns_len++] = (FFn){\"READ\", io_read};\n  book->ffns_buf[book->ffns_len++] = (FFn){\"OPEN\", io_open};\n  book->ffns_buf[book->ffns_len++] = (FFn){\"CLOSE\", io_close};\n  book->ffns_buf[book->ffns_len++] = (FFn){\"FLUSH\", io_flush};\n  book->ffns_buf[book->ffns_len++] = (FFn){\"WRITE\", io_write};\n  book->ffns_buf[book->ffns_len++] = (FFn){\"SEEK\", io_seek};\n  book->ffns_buf[book->ffns_len++] = (FFn){\"GET_TIME\", io_get_time};\n  book->ffns_buf[book->ffns_len++] = (FFn){\"SLEEP\", io_sleep};\n  book->ffns_buf[book->ffns_len++] = (FFn){\"DL_OPEN\", io_dl_open};\n  book->ffns_buf[book->ffns_len++] = (FFn){\"DL_CALL\", io_dl_call};\n  book->ffns_buf[book->ffns_len++] = (FFn){\"DL_CLOSE\", io_dl_open};\n\n  cudaMemcpyToSymbol(BOOK, book, sizeof(Book));\n}\n\n// Monadic IO Evaluator\n// ---------------------\n\n// Runs an IO computation.\nvoid do_run_io(GNet* gnet, Book* book, Port port) {\n  book_init(book);\n\n  setlinebuf(stdout);\n  setlinebuf(stderr);\n\n  // IO loop\n  while (true) {\n    // Normalizes the net\n    gnet_normalize(gnet);\n\n    // Reads the λ-Encoded Ctr\n    Ctr ctr = gnet_readback_ctr(gnet, gnet_peek(gnet, port));\n\n    // Checks if IO Magic Number is a CON\n    if (ctr.args_len < 1 || get_tag(ctr.args_buf[0]) != CON) {\n      break;\n    }\n\n    // Checks the IO Magic Number\n    Pair io_magic = gnet_node_load(gnet, get_val(ctr.args_buf[0]));\n    //printf(\"%08x %08x\\n\", get_u24(get_val(get_fst(io_magic))), get_u24(get_val(get_snd(io_magic))));\n    if (get_val(get_fst(io_magic)) != new_u24(IO_MAGIC_0) || get_val(get_snd(io_magic)) != new_u24(IO_MAGIC_1)) {\n      break;\n    }\n\n    switch (ctr.tag) {\n      case IO_CALL: {\n        if (ctr.args_len != 4) {\n          fprintf(stderr, \"invalid IO_CALL: args_len = %u\\n\", ctr.args_len);\n          break;\n        }\n\n        Str  func = gnet_readback_str(gnet, ctr.args_buf[1]);\n        FFn* ffn  = NULL;\n        // FIXME: optimize this linear search\n        for (u32 fid = 0; fid < book->ffns_len; ++fid) {\n          if (strcmp(func.buf, book->ffns_buf[fid].name) == 0) {\n            ffn = &book->ffns_buf[fid];\n            break;\n          }\n        }\n\n        free(func.buf);\n\n        Port argm = ctr.args_buf[2];\n        Port cont = ctr.args_buf[3];\n\n        Port ret;\n        if (ffn == NULL) {\n          ret = gnet_inject_io_err_name(gnet);\n        } else {\n          ret = ffn->func(gnet, argm);\n        }\n\n        Port p = gnet_make_node(gnet, CON, ret, ROOT);\n        gnet_boot_redex(gnet, new_pair(p, cont));\n        port = ROOT;\n\n        continue;\n      }\n\n      case IO_DONE: {\n        break;\n      }\n    }\n    break;\n  }\n}\n"
  },
  {
    "path": "tests/programs/empty.hvm",
    "content": ""
  },
  {
    "path": "tests/programs/hello-world.hvm",
    "content": "@String/Cons = (a (b ((@String/Cons/tag (a (b c))) c)))\n\n@String/Cons/tag = 1\n\n@String/Nil = ((@String/Nil/tag a) a)\n\n@String/Nil/tag = 0\n\n@main = l\n  & @String/Cons ~ (104 (k l))\n  & @String/Cons ~ (101 (j k))\n  & @String/Cons ~ (108 (i j))\n  & @String/Cons ~ (108 (h i))\n  & @String/Cons ~ (111 (g h))\n  & @String/Cons ~ (44 (f g))\n  & @String/Cons ~ (32 (e f))\n  & @String/Cons ~ (119 (d e))\n  & @String/Cons ~ (111 (c d))\n  & @String/Cons ~ (114 (b c))\n  & @String/Cons ~ (108 (a b))\n  & @String/Cons ~ (100 (@String/Nil a))\n\n\n"
  },
  {
    "path": "tests/programs/io/basic.bend",
    "content": "test-io = 1\n\ndef unwrap(res):\n  match res:\n    case Result/Ok:\n      return res.val\n    case Result/Err:\n      return res.val\n\ndef open():\n  return call(\"OPEN\", (\"./LICENSE\", \"r\"))\n\ndef read(f):\n  return call(\"READ\", (f, 47))\n\ndef print(bytes):\n  with IO:\n    * <- call(\"WRITE\", (1, bytes))\n    * <- call(\"WRITE\", (1, \"\\n\"))\n\n    return wrap(*)\n\ndef close(f):\n  return call(\"CLOSE\", f)\n\ndef main():\n  with IO:\n    f <- open()\n    f = unwrap(f)\n    bytes <- read(f)\n    bytes = unwrap(bytes)\n    * <- print(bytes)\n    res <- close(f)\n\n    return wrap(res)\n"
  },
  {
    "path": "tests/programs/io/basic.hvm",
    "content": "@IO/Call = (a (b (c (d ((@IO/Call/tag (a (b (c (d e))))) e)))))\n\n@IO/Call/tag = 1\n\n@IO/Done = (a (b ((@IO/Done/tag (a (b c))) c)))\n\n@IO/Done/tag = 0\n\n@IO/MAGIC = (13683217 16719857)\n\n@IO/bind = ((@IO/bind__C2 a) a)\n\n@IO/bind__C0 = (* (b (a c)))\n  & @undefer ~ (a (b c))\n\n@IO/bind__C1 = (* (* (a (b ((c d) (e g))))))\n  & @IO/Call ~ (@IO/MAGIC (a (b ((c f) g))))\n  & @IO/bind ~ (d (e f))\n\n@IO/bind__C2 = (?((@IO/bind__C0 @IO/bind__C1) a) a)\n\n@IO/wrap = a\n  & @IO/Done ~ (@IO/MAGIC a)\n\n@String/Cons = (a (b ((@String/Cons/tag (a (b c))) c)))\n\n@String/Cons/tag = 1\n\n@String/Nil = ((@String/Nil/tag a) a)\n\n@String/Nil/tag = 0\n\n@call = (a (b c))\n  & @IO/Call ~ (@IO/MAGIC (a (b (@call__C0 c))))\n\n@call__C0 = a\n  & @IO/Done ~ (@IO/MAGIC a)\n\n@close = f\n  & @call ~ (e f)\n  & @String/Cons ~ (67 (d e))\n  & @String/Cons ~ (76 (c d))\n  & @String/Cons ~ (79 (b c))\n  & @String/Cons ~ (83 (a b))\n  & @String/Cons ~ (69 (@String/Nil a))\n\n@main = w\n  & @IO/bind ~ (@open ((((s (a u)) (@IO/wrap v)) v) w))\n  & @IO/bind ~ (c ((((n (o (d q))) (r (s t))) t) u))\n  & @unwrap ~ (a {b r})\n  & @read ~ (b c)\n  & @IO/bind ~ (f ((((g (k (* m))) (n (o p))) p) q))\n  & @print ~ (e f)\n  & @unwrap ~ (d e)\n  & @IO/bind ~ (h ((((i i) (k l)) l) m))\n  & @close ~ (g h)\n\n@open = o\n  & @call ~ (d ((m n) o))\n  & @String/Cons ~ (79 (c d))\n  & @String/Cons ~ (80 (b c))\n  & @String/Cons ~ (69 (a b))\n  & @String/Cons ~ (78 (@String/Nil a))\n  & @String/Cons ~ (46 (l m))\n  & @String/Cons ~ (47 (k l))\n  & @String/Cons ~ (76 (j k))\n  & @String/Cons ~ (73 (i j))\n  & @String/Cons ~ (67 (h i))\n  & @String/Cons ~ (69 (g h))\n  & @String/Cons ~ (78 (f g))\n  & @String/Cons ~ (83 (e f))\n  & @String/Cons ~ (69 (@String/Nil e))\n  & @String/Cons ~ (114 (@String/Nil n))\n\n@print = (f h)\n  & @IO/bind ~ (g (@print__C3 h))\n  & @call ~ (e ((1 f) g))\n  & @String/Cons ~ (87 (d e))\n  & @String/Cons ~ (82 (c d))\n  & @String/Cons ~ (73 (b c))\n  & @String/Cons ~ (84 (a b))\n  & @String/Cons ~ (69 (@String/Nil a))\n\n@print__C0 = ((* a) (* a))\n\n@print__C1 = g\n  & @call ~ (e ((1 f) g))\n  & @String/Cons ~ (87 (d e))\n  & @String/Cons ~ (82 (c d))\n  & @String/Cons ~ (73 (b c))\n  & @String/Cons ~ (84 (a b))\n  & @String/Cons ~ (69 (@String/Nil a))\n  & @String/Cons ~ (10 (@String/Nil f))\n\n@print__C2 = (a (* c))\n  & @IO/bind ~ (@print__C1 (((@print__C0 (a b)) b) c))\n\n@print__C3 = ((@print__C2 (@IO/wrap a)) a)\n\n@read = (e f)\n  & @call ~ (d ((e 47) f))\n  & @String/Cons ~ (82 (c d))\n  & @String/Cons ~ (69 (b c))\n  & @String/Cons ~ (65 (a b))\n  & @String/Cons ~ (68 (@String/Nil a))\n\n@test-io = 1\n\n@undefer = (((a a) b) b)\n\n@unwrap = ((@unwrap__C0 a) a)\n\n@unwrap__C0 = (?(((a a) (* (b b))) c) c)\n\n\n"
  },
  {
    "path": "tests/programs/io/invalid-name.bend",
    "content": "test-io = 1\n\ndef main():\n  with IO:\n    f <- call(\"INVALID-NAME\", (\"./README.md\", \"r\"))\n\n    return wrap(f)\n"
  },
  {
    "path": "tests/programs/io/invalid-name.hvm",
    "content": "@IO/Call = (a (b (c (d ((@IO/Call/tag (a (b (c (d e))))) e)))))\n\n@IO/Call/tag = 1\n\n@IO/Done = (a (b ((@IO/Done/tag (a (b c))) c)))\n\n@IO/Done/tag = 0\n\n@IO/MAGIC = (13683217 16719857)\n\n@IO/bind = ((@IO/bind__C2 a) a)\n\n@IO/bind__C0 = (* (b (a c)))\n  & @undefer ~ (a (b c))\n\n@IO/bind__C1 = (* (* (a (b ((c d) (e g))))))\n  & @IO/Call ~ (@IO/MAGIC (a (b ((c f) g))))\n  & @IO/bind ~ (d (e f))\n\n@IO/bind__C2 = (?((@IO/bind__C0 @IO/bind__C1) a) a)\n\n@IO/wrap = a\n  & @IO/Done ~ (@IO/MAGIC a)\n\n@String/Cons = (a (b ((@String/Cons/tag (a (b c))) c)))\n\n@String/Cons/tag = 1\n\n@String/Nil = ((@String/Nil/tag a) a)\n\n@String/Nil/tag = 0\n\n@call = (a (b c))\n  & @IO/Call ~ (@IO/MAGIC (a (b (@call__C0 c))))\n\n@call__C0 = a\n  & @IO/Done ~ (@IO/MAGIC a)\n\n@main = cb\n  & @IO/bind ~ (y ((((z z) (@IO/wrap bb)) bb) cb))\n  & @call ~ (l ((w x) y))\n  & @String/Cons ~ (73 (k l))\n  & @String/Cons ~ (78 (j k))\n  & @String/Cons ~ (86 (i j))\n  & @String/Cons ~ (65 (h i))\n  & @String/Cons ~ (76 (g h))\n  & @String/Cons ~ (73 (f g))\n  & @String/Cons ~ (68 (e f))\n  & @String/Cons ~ (45 (d e))\n  & @String/Cons ~ (78 (c d))\n  & @String/Cons ~ (65 (b c))\n  & @String/Cons ~ (77 (a b))\n  & @String/Cons ~ (69 (@String/Nil a))\n  & @String/Cons ~ (46 (v w))\n  & @String/Cons ~ (47 (u v))\n  & @String/Cons ~ (82 (t u))\n  & @String/Cons ~ (69 (s t))\n  & @String/Cons ~ (65 (r s))\n  & @String/Cons ~ (68 (q r))\n  & @String/Cons ~ (77 (p q))\n  & @String/Cons ~ (69 (o p))\n  & @String/Cons ~ (46 (n o))\n  & @String/Cons ~ (109 (m n))\n  & @String/Cons ~ (100 (@String/Nil m))\n  & @String/Cons ~ (114 (@String/Nil x))\n\n@test-io = 1\n\n@undefer = (((a a) b) b)\n\n\n"
  },
  {
    "path": "tests/programs/io/open1.bend",
    "content": "test-io = 1\n\ndef main():\n  with IO:\n    f <- call(\"OPEN\", (\"./LICENSE\", \"r\"))\n\n    return wrap(f)\n"
  },
  {
    "path": "tests/programs/io/open1.hvm",
    "content": "@IO/Call = (a (b (c (d ((@IO/Call/tag (a (b (c (d e))))) e)))))\n\n@IO/Call/tag = 1\n\n@IO/Done = (a (b ((@IO/Done/tag (a (b c))) c)))\n\n@IO/Done/tag = 0\n\n@IO/MAGIC = (13683217 16719857)\n\n@IO/bind = ((@IO/bind__C2 a) a)\n\n@IO/bind__C0 = (* (b (a c)))\n  & @undefer ~ (a (b c))\n\n@IO/bind__C1 = (* (* (a (b ((c d) (e g))))))\n  & @IO/Call ~ (@IO/MAGIC (a (b ((c f) g))))\n  & @IO/bind ~ (d (e f))\n\n@IO/bind__C2 = (?((@IO/bind__C0 @IO/bind__C1) a) a)\n\n@IO/wrap = a\n  & @IO/Done ~ (@IO/MAGIC a)\n\n@String/Cons = (a (b ((@String/Cons/tag (a (b c))) c)))\n\n@String/Cons/tag = 1\n\n@String/Nil = ((@String/Nil/tag a) a)\n\n@String/Nil/tag = 0\n\n@call = (a (b c))\n  & @IO/Call ~ (@IO/MAGIC (a (b (@call__C0 c))))\n\n@call__C0 = a\n  & @IO/Done ~ (@IO/MAGIC a)\n\n@main = s\n  & @IO/bind ~ (o ((((p p) (@IO/wrap r)) r) s))\n  & @call ~ (d ((m n) o))\n  & @String/Cons ~ (79 (c d))\n  & @String/Cons ~ (80 (b c))\n  & @String/Cons ~ (69 (a b))\n  & @String/Cons ~ (78 (@String/Nil a))\n  & @String/Cons ~ (46 (l m))\n  & @String/Cons ~ (47 (k l))\n  & @String/Cons ~ (76 (j k))\n  & @String/Cons ~ (73 (i j))\n  & @String/Cons ~ (67 (h i))\n  & @String/Cons ~ (69 (g h))\n  & @String/Cons ~ (78 (f g))\n  & @String/Cons ~ (83 (e f))\n  & @String/Cons ~ (69 (@String/Nil e))\n  & @String/Cons ~ (114 (@String/Nil n))\n\n@test-io = 1\n\n@undefer = (((a a) b) b)\n\n\n"
  },
  {
    "path": "tests/programs/io/open2.bend",
    "content": "test-io = 1\n\ndef main():\n  with IO:\n    f <- call(\"OPEN\", (\"fake-file\", \"r\"))\n\n    return wrap(f)\n"
  },
  {
    "path": "tests/programs/io/open2.hvm",
    "content": "@IO/Call = (a (b (c (d ((@IO/Call/tag (a (b (c (d e))))) e)))))\n\n@IO/Call/tag = 1\n\n@IO/Done = (a (b ((@IO/Done/tag (a (b c))) c)))\n\n@IO/Done/tag = 0\n\n@IO/MAGIC = (13683217 16719857)\n\n@IO/bind = ((@IO/bind__C2 a) a)\n\n@IO/bind__C0 = (* (b (a c)))\n  & @undefer ~ (a (b c))\n\n@IO/bind__C1 = (* (* (a (b ((c d) (e g))))))\n  & @IO/Call ~ (@IO/MAGIC (a (b ((c f) g))))\n  & @IO/bind ~ (d (e f))\n\n@IO/bind__C2 = (?((@IO/bind__C0 @IO/bind__C1) a) a)\n\n@IO/wrap = a\n  & @IO/Done ~ (@IO/MAGIC a)\n\n@String/Cons = (a (b ((@String/Cons/tag (a (b c))) c)))\n\n@String/Cons/tag = 1\n\n@String/Nil = ((@String/Nil/tag a) a)\n\n@String/Nil/tag = 0\n\n@call = (a (b c))\n  & @IO/Call ~ (@IO/MAGIC (a (b (@call__C0 c))))\n\n@call__C0 = a\n  & @IO/Done ~ (@IO/MAGIC a)\n\n@main = s\n  & @IO/bind ~ (o ((((p p) (@IO/wrap r)) r) s))\n  & @call ~ (d ((m n) o))\n  & @String/Cons ~ (79 (c d))\n  & @String/Cons ~ (80 (b c))\n  & @String/Cons ~ (69 (a b))\n  & @String/Cons ~ (78 (@String/Nil a))\n  & @String/Cons ~ (102 (l m))\n  & @String/Cons ~ (97 (k l))\n  & @String/Cons ~ (107 (j k))\n  & @String/Cons ~ (101 (i j))\n  & @String/Cons ~ (45 (h i))\n  & @String/Cons ~ (102 (g h))\n  & @String/Cons ~ (105 (f g))\n  & @String/Cons ~ (108 (e f))\n  & @String/Cons ~ (101 (@String/Nil e))\n  & @String/Cons ~ (114 (@String/Nil n))\n\n@test-io = 1\n\n@undefer = (((a a) b) b)\n\n\n"
  },
  {
    "path": "tests/programs/io/open3.bend",
    "content": "test-io = 1\n\ndef main():\n  with IO:\n    # calling open with an unexpected type of arg\n    f <- call(\"OPEN\", 123)\n\n    return wrap(f)\n"
  },
  {
    "path": "tests/programs/io/open3.hvm",
    "content": "@IO/Call = (a (b (c (d ((@IO/Call/tag (a (b (c (d e))))) e)))))\n\n@IO/Call/tag = 1\n\n@IO/Done = (a (b ((@IO/Done/tag (a (b c))) c)))\n\n@IO/Done/tag = 0\n\n@IO/MAGIC = (13683217 16719857)\n\n@IO/bind = ((@IO/bind__C2 a) a)\n\n@IO/bind__C0 = (* (b (a c)))\n  & @undefer ~ (a (b c))\n\n@IO/bind__C1 = (* (* (a (b ((c d) (e g))))))\n  & @IO/Call ~ (@IO/MAGIC (a (b ((c f) g))))\n  & @IO/bind ~ (d (e f))\n\n@IO/bind__C2 = (?((@IO/bind__C0 @IO/bind__C1) a) a)\n\n@IO/wrap = a\n  & @IO/Done ~ (@IO/MAGIC a)\n\n@String/Cons = (a (b ((@String/Cons/tag (a (b c))) c)))\n\n@String/Cons/tag = 1\n\n@String/Nil = ((@String/Nil/tag a) a)\n\n@String/Nil/tag = 0\n\n@call = (a (b c))\n  & @IO/Call ~ (@IO/MAGIC (a (b (@call__C0 c))))\n\n@call__C0 = a\n  & @IO/Done ~ (@IO/MAGIC a)\n\n@main = i\n  & @IO/bind ~ (e ((((f f) (@IO/wrap h)) h) i))\n  & @call ~ (d (123 e))\n  & @String/Cons ~ (79 (c d))\n  & @String/Cons ~ (80 (b c))\n  & @String/Cons ~ (69 (a b))\n  & @String/Cons ~ (78 (@String/Nil a))\n\n@test-io = 1\n\n@undefer = (((a a) b) b)\n\n\n"
  },
  {
    "path": "tests/programs/list.hvm",
    "content": "@List/Cons = (a (b ((@List/Cons/tag (a (b c))) c)))\n\n@List/Cons/tag = 1\n\n@List/Nil = ((@List/Nil/tag a) a)\n\n@List/Nil/tag = 0\n\n@id = (a a)\n\n@list = c\n  & @List/Cons ~ (1 (b c))\n  & @List/Cons ~ (2 (a b))\n  & @List/Cons ~ (3 (@List/Nil a))\n\n@main = e\n  & @map ~ ((a b) (d e))\n  & @map ~ (a (@list b))\n  & @List/Cons ~ (@id (@List/Nil d))\n\n// @main__C0 = (a b)\n//   & @map ~ (a (@list b))\n\n@map = (a ((@map__C1 (a b)) b))\n\n@map__C0 = (* (a (d ({(a b) c} f))))\n  & @List/Cons ~ (b (e f))\n  & @map ~ (c (d e))\n\n@map__C1 = (?(((* @List/Nil) @map__C0) a) a)\n"
  },
  {
    "path": "tests/programs/numeric-casts.hvm",
    "content": "@main = x & @tu0 ~ (* x)\n\n// casting to u24\n@tu0 = (* {n x}) & @tu1 ~ (* x) &  0          ~ $([u24] n) // 0\n@tu1 = (* {n x}) & @tu2 ~ (* x) &  1234       ~ $([u24] n) // 1234\n@tu2 = (* {n x}) & @tu3 ~ (* x) & +4321       ~ $([u24] n) // 4321\n@tu3 = (* {n x}) & @tu4 ~ (* x) & -5678       ~ $([u24] n) // 16771538 (reinterprets bits)\n@tu4 = (* {n x}) & @tu5 ~ (* x) &  2.8        ~ $([u24] n) // 2 (rounds to zero)\n@tu5 = (* {n x}) & @tu6 ~ (* x) & -12.5       ~ $([u24] n) // 0 (saturates)\n@tu6 = (* {n x}) & @tu7 ~ (* x) &  16777216.0 ~ $([u24] n) // 16777215 (saturates)\n@tu7 = (* {n x}) & @tu8 ~ (* x) & +inf        ~ $([u24] n) // 16777215 (saturates)\n@tu8 = (* {n x}) & @tu9 ~ (* x) & -inf        ~ $([u24] n) // 0 (saturates)\n@tu9 = (* {n x}) & @ti0 ~ (* x) & +NaN        ~ $([u24] n) // 0\n\n// casting to i24\n@ti0 = (* {n x}) & @ti1 ~ (* x) &  0          ~ $([i24] n) // +0\n@ti1 = (* {n x}) & @ti2 ~ (* x) &  1234       ~ $([i24] n) // +1234\n@ti2 = (* {n x}) & @ti3 ~ (* x) & +4321       ~ $([i24] n) // +4321\n@ti3 = (* {n x}) & @ti4 ~ (* x) & -5678       ~ $([i24] n) // -5678\n@ti4 = (* {n x}) & @ti5 ~ (* x) &  2.8        ~ $([i24] n) // +2   (rounds to zero)\n@ti5 = (* {n x}) & @ti6 ~ (* x) & -12.7       ~ $([i24] n) // -12 (rounds to zero)\n@ti6 = (* {n x}) & @ti7 ~ (* x) &  8388610.0  ~ $([i24] n) // +8388607 (saturates)\n@ti7 = (* {n x}) & @ti8 ~ (* x) & -8388610.0  ~ $([i24] n) // -8388608 (saturates)\n@ti8 = (* {n x}) & @ti9 ~ (* x) & +inf        ~ $([i24] n) // +8388607 (saturates)\n@ti9 = (* {n x}) & @ti10 ~ (* x) & -inf        ~ $([i24] n) // -8388608 (saturates)\n@ti10 = (* {n x}) & @tf0 ~ (* x) & +NaN        ~ $([i24] n) // +0\n\n// casting to f24\n@tf0 = (* {n x}) & @tf1 ~ (* x) &  +NaN    ~ $([f24] n) // +NaN\n@tf1 = (* {n x}) & @tf2 ~ (* x) &  +inf    ~ $([f24] n) // +inf\n@tf2 = (* {n x}) & @tf3 ~ (* x) &  -inf    ~ $([f24] n) // -inf\n@tf3 = (* {n x}) & @tf4 ~ (* x) &  2.15    ~ $([f24] n) //  2.15\n@tf4 = (* {n x}) & @tf5 ~ (* x) & -2.15    ~ $([f24] n) // -2.15\n@tf5 = (* {n x}) & @tf6 ~ (* x) &  0.15    ~ $([f24] n) //  0.15\n@tf6 = (* {n x}) & @tf7 ~ (* x) & -1234    ~ $([f24] n) // -1234.0\n@tf7 = (* {n x}) & @tf8 ~ (* x) & +1234    ~ $([f24] n) // +1234.0\n@tf8 = (* {n x}) & @tf9 ~ (* x) & 123456   ~ $([f24] n) // 123456.0\n@tf9 = (* {n x}) & @tp0 ~ (* x) & 16775982 ~ $([f24] n) // 16775936.0\n\n// printing\n@tp0 = (* {n x}) & @tp1 ~ (* x) & n ~ [u24] // [u24]\n@tp1 = (* {n x}) & @tp2 ~ (* x) & n ~ [i24] // [i24]\n@tp2 = (* {n x}) & @t   ~ (* x) & n ~ [f24] // [f24]\n\n@t = *\n"
  },
  {
    "path": "tests/programs/numerics/f24.hvm",
    "content": "@half = xN & 1.0 ~ $([/] $(2.0 xN))\n@nan = xN & 0.0 ~ $([/] $(0.0 xN))\n\n@main = x & @t0 ~ (* x)\n\n// nan and inf divisions\n@t0 = (* {n x}) & @t1 ~ (* x) & 1.0  ~ $([/] $(0.0 n)) // +inf\n@t1 = (* {n x}) & @t2 ~ (* x) & -1.0 ~ $([/] $(0.0 n)) // -inf\n@t2 = (* {n x}) & @t3 ~ (* x) & 0.0  ~ $([/] $(0.0 n)) // NaN\n\n// general operators\n@t3 = (* {n x}) & @t4 ~ (* x) & @half ~ $([+] $(2.0 n)) // 2.5\n@t4 = (* {n x}) & @t5 ~ (* x) & @half ~ $([-] $(2.0 n)) // -1.5\n@t5 = (* {n x}) & @t6 ~ (* x) & @half ~ $([*] $(2.3 n)) // 1.15\n@t6 = (* {n x}) & @t7 ~ (* x) & @half ~ $([/] $(2.0 n)) // 0.25\n@t7 = (* {n x}) & @t8 ~ (* x) & @half ~ $([%] $(2.0 n)) // 0.5\n\n// comparisons (returning ints)\n@t8 = (* {n x}) & @t9 ~ (* x) & @half ~ $([=] $(2.0 n)) // 0\n@t9 = (* {n x}) & @tA ~ (* x) & @half ~ $([!] $(2.0 n)) // 1\n@tA = (* {n x}) & @tB ~ (* x) & @half ~ $([<] $(2.0 n)) // 1\n@tB = (* {n x}) & @tC ~ (* x) & @half ~ $([>] $(2.0 n)) // 0\n\n// ieee nan comparisons\n@tC = (* {n x}) & @tD ~ (* x) & @nan ~ $([=] $(@nan n)) // 0\n@tD = (* {n x}) & @tE ~ (* x) & @nan ~ $([<] $(@nan n)) // 0\n@tE = (* {n x}) & @tF ~ (* x) & @nan ~ $([>] $(@nan n)) // 0\n\n// parsing\n@tF = (* {n x}) & @tG ~ (* x) & +NaN ~ $([+] $(0.0 n)) // NaN\n@tG = (* {n x}) & @tH ~ (* x) & +inf ~ $([+] $(0.0 n)) // inf\n@tH = (* {n x}) & @tI ~ (* x) & -inf ~ $([+] $(0.0 n)) // -inf\n@tI = (* {n x}) & @tJ ~ (* x) & 1.02 ~ $([+] $(0.0 n)) // 1.02\n\n// modulo\n@tJ = (* {n x}) & @tK ~ (* x) & +1.2 ~ $([%] $(+1.1 n)) // +0.1\n@tK = (* {n x}) & @tL ~ (* x) & +1.2 ~ $([%] $(-1.1 n)) // +0.1\n@tL = (* {n x}) & @tM ~ (* x) & -1.2 ~ $([%] $(+1.1 n)) // -0.1\n@tM = (* {n x}) & @tN ~ (* x) & -1.2 ~ $([%] $(-1.1 n)) // -0.1\n\n// modulo\n@tN = (* {n x}) & @tO ~ (* x) & +0.0         ~ $([<<] $(+3.14159265 n)) // ~0\n@tO = (* {n x}) & @tP ~ (* x) & +1.570796325 ~ $([<<] $(+0.0 n))        // 1.0\n@tP = (* {n x}) & @tQ ~ (* x) & +0.0         ~ $([>>] $(+3.14159265 n)) // ~0\n@tQ = (* {n x}) & @tR ~ (* x) & +0.785398162 ~ $([>>] $(+0.0 n))        // 1.0\n\n@tR = *\n"
  },
  {
    "path": "tests/programs/numerics/i24.hvm",
    "content": "@main = x & @t0 ~ (* x)\n\n// all ops\n@t0 = (* {n x}) & @t1 ~ (* x) & +10 ~ $([+]  $(+2 n)) // 12\n@t1 = (* {n x}) & @t2 ~ (* x) & +10 ~ $([-]  $(+2 n)) // 8\n@t2 = (* {n x}) & @t3 ~ (* x) & +10 ~ $([*]  $(+2 n)) // 20\n@t3 = (* {n x}) & @t4 ~ (* x) & +10 ~ $([/]  $(+2 n)) // 5\n@t4 = (* {n x}) & @t5 ~ (* x) & +10 ~ $([%]  $(+2 n)) // 0\n@t5 = (* {n x}) & @t6 ~ (* x) & +10 ~ $([=]  $(+2 n)) // 0\n@t6 = (* {n x}) & @t7 ~ (* x) & +10 ~ $([!]  $(+2 n)) // 1\n@t7 = (* {n x}) & @t8 ~ (* x) & +10 ~ $([<]  $(+2 n)) // 0\n@t8 = (* {n x}) & @t9 ~ (* x) & +10 ~ $([>]  $(+2 n)) // 1\n@t9 = (* {n x}) & @tA ~ (* x) & +10 ~ $([&]  $(+2 n)) // 2\n@tA = (* {n x}) & @tB ~ (* x) & +10 ~ $([|]  $(+2 n)) // 10\n@tB = (* {n x}) & @tC ~ (* x) & +10 ~ $([^]  $(+2 n)) // 8\n  \n// underflow\n@tC = (* {n x}) & @tD ~ (* x) & -8388608 ~ $([-] $(+1 n)) // 8388607\n\n// overflow\n@tD = (* {n x}) & @tE ~ (* x) & +8388607 ~ $([+] $(+1 n)) // -8388608\n\n// modulo\n@tE = (* {n x}) & @tF ~ (* x) & +3 ~ $([%] $(+2 n)) // +1\n@tF = (* {n x}) & @tG ~ (* x) & +3 ~ $([%] $(-2 n)) // +1\n@tG = (* {n x}) & @tH ~ (* x) & -3 ~ $([%] $(+2 n)) // -1\n@tH = (* {n x}) & @tI ~ (* x) & -3 ~ $([%] $(-2 n)) // -1\n\n@tI = *\n\n"
  },
  {
    "path": "tests/programs/numerics/u24.hvm",
    "content": "@main = x & @t0 ~ (* x)\n\n// all ops\n@t0 = (* {n x}) & @t1 ~ (* x) & 10 ~ $([+]  $(2 n)) // 12\n@t1 = (* {n x}) & @t2 ~ (* x) & 10 ~ $([-]  $(2 n)) // 8\n@t2 = (* {n x}) & @t3 ~ (* x) & 10 ~ $([*]  $(2 n)) // 20\n@t3 = (* {n x}) & @t4 ~ (* x) & 10 ~ $([/]  $(2 n)) // 5\n@t4 = (* {n x}) & @t5 ~ (* x) & 10 ~ $([%]  $(2 n)) // 0\n@t5 = (* {n x}) & @t6 ~ (* x) & 10 ~ $([=]  $(2 n)) // 0\n@t6 = (* {n x}) & @t7 ~ (* x) & 10 ~ $([!]  $(2 n)) // 1\n@t7 = (* {n x}) & @t8 ~ (* x) & 10 ~ $([<]  $(2 n)) // 0\n@t8 = (* {n x}) & @t9 ~ (* x) & 10 ~ $([>]  $(2 n)) // 1\n@t9 = (* {n x}) & @tA ~ (* x) & 10 ~ $([&]  $(2 n)) // 2\n@tA = (* {n x}) & @tB ~ (* x) & 10 ~ $([|]  $(2 n)) // 10\n@tB = (* {n x}) & @tC ~ (* x) & 10 ~ $([^]  $(2 n)) // 8\n@tC = (* {n x}) & @tD ~ (* x) & 10 ~ $([<<] $(2 n)) // 40\n@tD = (* {n x}) & @tE ~ (* x) & 10 ~ $([>>] $(2 n)) // 2\n\n// underflow\n@tE = (* {n x}) & @tF ~ (* x) & 0 ~ $([-] $(1 n)) // 16777215\n\n// overflow\n@tF = (* {n x}) & @tG ~ (* x) & 16777215 ~ $([+] $(1 n)) // 0\n\n// no sign extension\n@tG = (* {n x}) & @tH ~ (* x) & 16777215 ~ $([>>] $(22 n)) // 3\n\n@tH = *\n"
  },
  {
    "path": "tests/programs/safety-check.hvm",
    "content": "@List/Cons = (a (b ((@List/Cons/tag (a (b c))) c)))\n\n@List/Cons/tag = 1\n\n@List/Nil = ((@List/Nil/tag a) a)\n\n@List/Nil/tag = 0\n\n@id = (a a)\n\n@list = c\n  & @List/Cons ~ (1 (b c))\n  & @List/Cons ~ (2 (@List/Nil b))\n\n@main = b\n  & @map ~ (@main__C0 (a b))\n  & @List/Cons ~ (@id (@List/Nil a))\n\n@main__C0 = (a b)\n  & @map ~ (a (@list b))\n\n@map = (a ((@map__C1 (a b)) b))\n\n@map__C0 = (* (a (d ({(a b) c} f))))\n  & @List/Cons ~ (b (e f))\n  & @map ~ (c (d e))\n\n@map__C1 = (?(((* @List/Nil) @map__C0) a) a)\n\n// Test flags\n@test-rust-only = 1"
  },
  {
    "path": "tests/run.rs",
    "content": "use std::{\n  collections::HashMap,\n  error::Error,\n  ffi::OsStr,\n  fs,\n  io::{Read, Write},\n  path::{Path, PathBuf},\n  process::{Command, Stdio},\n};\n\nuse hvm::ast::Tree;\nuse insta::assert_snapshot;\nuse TSPL::Parser;\n\n#[test]\nfn test_run_programs() {\n  test_dir(&manifest_relative(\"tests/programs/\"));\n}\n\n#[test]\nfn test_run_examples() {\n  test_dir(&manifest_relative(\"examples/\"));\n}\n\nfn test_dir(dir: &Path) {\n  insta::glob!(dir, \"**/*.hvm\", test_file)\n}\n\nfn manifest_relative(sub: &str) -> PathBuf {\n  format!(\"{}/{}\", env!(\"CARGO_MANIFEST_DIR\"), sub).into()\n}\n\nfn test_file(path: &Path) {\n  let contents = fs::read_to_string(path).unwrap();\n  if contents.contains(\"@test-skip = 1\") {\n    println!(\"skipping {path:?}\");\n    return;\n  }\n  if contents.contains(\"@test-io = 1\") {\n    test_io_file(path);\n    return;\n  }\n\n  println!(\"testing {path:?}...\");\n  let rust_output = execute_hvm(&[\"run\".as_ref(), path.as_os_str()], false).unwrap();\n  assert_snapshot!(rust_output);\n\n  if contents.contains(\"@test-rust-only = 1\") {\n    println!(\"only testing rust implementation for {path:?}\");\n    return;\n  }\n\n  println!(\"  testing {path:?}, C...\");\n  let c_output = execute_hvm(&[\"run-c\".as_ref(), path.as_os_str()], false).unwrap();\n  assert_eq!(c_output, rust_output, \"{path:?}: C output does not match rust output\");\n\n  if cfg!(feature = \"cuda\") {\n    println!(\"  testing {path:?}, CUDA...\");\n    let cuda_output = execute_hvm(&[\"run-cu\".as_ref(), path.as_os_str()], false).unwrap();\n    assert_eq!(\n      cuda_output, rust_output,\n      \"{path:?}: CUDA output does not match rust output\"\n    );\n  }\n}\n\nfn test_io_file(path: &Path) {\n  println!(\"  testing (io) {path:?}, C...\");\n  let c_output = execute_hvm(&[\"run-c\".as_ref(), path.as_os_str()], true).unwrap();\n  assert_snapshot!(c_output);\n\n  if cfg!(feature = \"cuda\") {\n    println!(\"  testing (io) {path:?}, CUDA...\");\n    let cuda_output = execute_hvm(&[\"run-cu\".as_ref(), path.as_os_str()], true).unwrap();\n    assert_eq!(cuda_output, c_output, \"{path:?}: CUDA output does not match C output\");\n  }\n}\n\nfn execute_hvm(args: &[&OsStr], send_io: bool) -> Result<String, Box<dyn Error>> {\n  // Spawn the command\n  let mut child = Command::new(env!(\"CARGO_BIN_EXE_hvm\"))\n    .args(args)\n    .stdin(Stdio::piped())\n    .stdout(Stdio::piped())\n    .stderr(Stdio::piped())\n    .spawn()?;\n\n  // Capture the output of the command\n  let mut stdout = child.stdout.take().ok_or(\"Couldn't capture stdout!\")?;\n  let mut stderr = child.stderr.take().ok_or(\"Couldn't capture stderr!\")?;\n\n  // Wait for the command to finish and get the exit status\n  if send_io {\n    let mut stdin = child.stdin.take().ok_or(\"Couldn't capture stdin!\")?;\n    stdin.write_all(b\"io from the tests\\n\")?;\n    drop(stdin);\n  }\n  let status = child.wait()?;\n\n  // Read the output\n  let mut output = String::new();\n  stdout.read_to_string(&mut output)?;\n  stderr.read_to_string(&mut output)?;\n\n  Ok(if !status.success() {\n    format!(\"{status}\\n{output}\")\n  } else {\n    parse_output(&output).unwrap_or_else(|err| panic!(\"error parsing output:\\n{err}\\n\\n{output}\"))\n  })\n}\n\nfn parse_output(output: &str) -> Result<String, String> {\n  let mut lines = Vec::new();\n\n  for line in output.lines() {\n    if line.starts_with(\"Result:\") {\n      let mut parser = hvm::ast::CoreParser::new(line);\n      parser.consume(\"Result:\")?;\n      let mut tree = parser.parse_tree()?;\n      normalize_vars(&mut tree, &mut HashMap::new());\n      lines.push(format!(\"Result: {}\", tree.show()));\n    } else if !line.starts_with(\"- ITRS:\")\n      && !line.starts_with(\"- TIME:\")\n      && !line.starts_with(\"- MIPS:\")\n      && !line.starts_with(\"- LEAK:\")\n    {\n      // TODO: include iteration count in snapshot once consistent\n      lines.push(line.to_string())\n    }\n  }\n\n  Ok(lines.join(\"\\n\"))\n}\n\nfn normalize_vars(tree: &mut Tree, vars: &mut HashMap<String, usize>) {\n  match tree {\n    Tree::Var { nam } => {\n      let next_var = vars.len();\n      *nam = format!(\"x{}\", vars.entry(std::mem::take(nam)).or_insert(next_var));\n    }\n    Tree::Era | Tree::Ref { .. } | Tree::Num { .. } => {}\n    Tree::Con { fst, snd } | Tree::Dup { fst, snd } | Tree::Opr { fst, snd } | Tree::Swi { fst, snd } => {\n      normalize_vars(fst, vars);\n      normalize_vars(snd, vars);\n    }\n  }\n}\n"
  },
  {
    "path": "tests/snapshots/run__file@empty.hvm.snap",
    "content": "---\nsource: tests/run.rs\nexpression: rust_output\ninput_file: tests/programs/empty.hvm\n---\nexit status: 101\nthread 'main' panicked at src/ast.rs:545:41:\nmissing `@main` definition\nnote: run with `RUST_BACKTRACE=1` environment variable to display a backtrace\n"
  },
  {
    "path": "tests/snapshots/run__file@hello-world.hvm.snap",
    "content": "---\nsource: tests/run.rs\nexpression: rust_output\ninput_file: tests/programs/hello-world.hvm\n---\nResult: ((@String/Cons/tag (104 (((@String/Cons/tag (101 (((@String/Cons/tag (108 (((@String/Cons/tag (108 (((@String/Cons/tag (111 (((@String/Cons/tag (44 (((@String/Cons/tag (32 (((@String/Cons/tag (119 (((@String/Cons/tag (111 (((@String/Cons/tag (114 (((@String/Cons/tag (108 (((@String/Cons/tag (100 (@String/Nil x0))) x0) x1))) x1) x2))) x2) x3))) x3) x4))) x4) x5))) x5) x6))) x6) x7))) x7) x8))) x8) x9))) x9) x10))) x10) x11))) x11)\n"
  },
  {
    "path": "tests/snapshots/run__file@list.hvm.snap",
    "content": "---\nsource: tests/run.rs\nexpression: rust_output\ninput_file: tests/programs/list.hvm\n---\nResult: ((@List/Cons/tag (((@List/Cons/tag (1 (((@List/Cons/tag (* (((@List/Cons/tag (* (@List/Nil x0))) x0) x1))) x1) x2))) x2) (@List/Nil x3))) x3)\n"
  },
  {
    "path": "tests/snapshots/run__file@numeric-casts.hvm.snap",
    "content": "---\nsource: tests/run.rs\nexpression: rust_output\ninput_file: tests/programs/numeric-casts.hvm\n---\nResult: {0 {1234 {4321 {16771538 {2 {0 {16777215 {16777215 {0 {0 {+0 {+1234 {+4321 {-5678 {+2 {-12 {+8388607 {-8388608 {+8388607 {-8388608 {+0 {+NaN {+inf {-inf {2.1500244 {-2.1500244 {0.15000153 {-1234.0 {1234.0 {123456.0 {16775936.0 {[u24] {[i24] {[f24] *}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}\n"
  },
  {
    "path": "tests/snapshots/run__file@numerics__f24.hvm.snap",
    "content": "---\nsource: tests/run.rs\nexpression: rust_output\ninput_file: tests/programs/numerics/f24.hvm\n---\nResult: {+inf {-inf {+NaN {2.5 {-1.5 {1.1499939 {0.25 {0.5 {0 {1 {1 {0 {0 {0 {0 {+NaN {+inf {-inf {1.019989 {0.1000061 {0.1000061 {-0.1000061 {-0.1000061 {-8.908799e-6 {1.0 {8.908799e-6 {1.0 *}}}}}}}}}}}}}}}}}}}}}}}}}}}\n"
  },
  {
    "path": "tests/snapshots/run__file@numerics__i24.hvm.snap",
    "content": "---\nsource: tests/run.rs\nexpression: rust_output\ninput_file: tests/programs/i24.hvm\n---\nResult: {+12 {+8 {+20 {+5 {+0 {0 {1 {0 {1 {+2 {+10 {+8 {+8388607 {-8388608 {+1 {+1 {-1 {-1 *}}}}}}}}}}}}}}}}}}\n"
  },
  {
    "path": "tests/snapshots/run__file@numerics__u24.hvm.snap",
    "content": "---\nsource: tests/run.rs\nexpression: rust_output\ninput_file: tests/programs/u24.hvm\n---\nResult: {12 {8 {20 {5 {0 {0 {1 {0 {1 {2 {10 {8 {40 {2 {16777215 {0 {3 *}}}}}}}}}}}}}}}}}\n"
  },
  {
    "path": "tests/snapshots/run__file@safety-check.hvm.snap",
    "content": "---\nsource: tests/run.rs\nexpression: rust_output\ninput_file: tests/programs/safety-check.hvm\n---\nERROR: attempt to clone a non-affine global reference.\n"
  },
  {
    "path": "tests/snapshots/run__file@sort_bitonic__main.hvm.snap",
    "content": "---\nsource: tests/run.rs\nexpression: rust_output\ninput_file: examples/sort_bitonic/main.hvm\n---\nResult: 8386560\n"
  },
  {
    "path": "tests/snapshots/run__file@sort_radix__main.hvm.snap",
    "content": "---\nsource: tests/run.rs\nexpression: rust_output\ninput_file: examples/sort_radix/main.hvm\n---\nResult: 16744448\n"
  },
  {
    "path": "tests/snapshots/run__file@stress__main.hvm.snap",
    "content": "---\nsource: tests/run.rs\nexpression: rust_output\ninput_file: examples/stress/main.hvm\n---\nResult: 0\n"
  },
  {
    "path": "tests/snapshots/run__file@sum_rec__main.hvm.snap",
    "content": "---\nsource: tests/run.rs\nexpression: rust_output\ninput_file: examples/sum_rec/main.hvm\n---\nResult: 16252928\n"
  },
  {
    "path": "tests/snapshots/run__file@sum_tree__main.hvm.snap",
    "content": "---\nsource: tests/run.rs\nexpression: rust_output\ninput_file: examples/sum_tree/main.hvm\n---\nResult: 1048576\n"
  },
  {
    "path": "tests/snapshots/run__file@tuples__tuples.hvm.snap",
    "content": "---\nsource: tests/run.rs\nexpression: rust_output\ninput_file: examples/tuples/tuples.hvm\n---\nResult: ((0 (3 (4 (5 (6 (7 (8 (1 (2 x0))))))))) x0)\n"
  },
  {
    "path": "tests/snapshots/run__io_file@demo_io__main.hvm.snap",
    "content": "---\nsource: tests/run.rs\nexpression: c_output\ninput_file: examples/demo_io/main.hvm\n---\n                                 Apache License\nResult: ((@IO/Done/tag (@IO/MAGIC (((0 (* x0)) x0) x1))) x1)\n"
  },
  {
    "path": "tests/snapshots/run__io_file@io__basic.hvm.snap",
    "content": "---\nsource: tests/run.rs\nexpression: c_output\ninput_file: tests/programs/io/basic.hvm\n---\n                                 Apache License\nResult: ((@IO/Done/tag (@IO/MAGIC (((0 (* x0)) x0) x1))) x1)\n"
  },
  {
    "path": "tests/snapshots/run__io_file@io__invalid-name.hvm.snap",
    "content": "---\nsource: tests/run.rs\nexpression: c_output\ninput_file: tests/programs/io/invalid-name.hvm\n---\nResult: ((@IO/Done/tag (@IO/MAGIC (((1 (((1 x0) x0) x1)) x1) x2))) x2)\n"
  },
  {
    "path": "tests/snapshots/run__io_file@io__open1.hvm.snap",
    "content": "---\nsource: tests/run.rs\nexpression: c_output\ninput_file: tests/programs/io/open1.hvm\n---\nResult: ((@IO/Done/tag (@IO/MAGIC (((0 (3 x0)) x0) x1))) x1)\n"
  },
  {
    "path": "tests/snapshots/run__io_file@io__open2.hvm.snap",
    "content": "---\nsource: tests/run.rs\nexpression: c_output\ninput_file: tests/programs/io/open2.hvm\n---\nResult: ((@IO/Done/tag (@IO/MAGIC (((1 (((2 (+2 x0)) x0) x1)) x1) x2))) x2)\n"
  },
  {
    "path": "tests/snapshots/run__io_file@io__open3.hvm.snap",
    "content": "---\nsource: tests/run.rs\nexpression: c_output\ninput_file: tests/programs/io/open3.hvm\n---\nResult: ((@IO/Done/tag (@IO/MAGIC (((1 (((0 x0) x0) x1)) x1) x2))) x2)\n"
  },
  {
    "path": "tests/snapshots/run__io_file@io__read_and_print.hvm.snap",
    "content": "---\nsource: tests/run.rs\nexpression: c_output\ninput_file: tests/programs/io/read_and_print.hvm\n---\nWhat is your name?\nio fr from\nResult: 42\n"
  }
]